Quickstart
Go from zero to your first deal in 5 minutes. Create a server-side API key, generate a widget publishable key, embed the widget, and start closing deals.
Pick a Starting Surface
Salesbooth exposes more than one API shape. Start with the smallest spec that matches what you are building.
| Surface | Best for | Spec |
|---|---|---|
| Core Commerce API | Customers, products, deals, contracts, payments, webhooks, and commerce schemas | /api/core-openapi.json |
| Widget API | Widget config, saved configs, pricing previews, validation, analytics, and bookings | /api/widget-openapi.json |
| Agent API | Discovery, negotiation, delegation, trust, tools, and workflows | /api/agent-openapi.json |
| Legacy Public Alias | The filtered public-plus-widget compatibility document used by earlier tooling | /api/public-openapi.json |
| Public Compatibility API | The default public OpenAPI document for external integrations, combining core commerce and widget surfaces without admin or internal routes. | /api/openapi.json |
Build This First
Most teams should begin with one concrete flow instead of reading the full reference front to back.
Create customer → create deal → add products → send contract → collect payment. Use this when your server owns the checkout flow and Salesbooth manages the deal lifecycle behind it.
Embed widget → save config → convert to deal. Use this when you want browser-led product configuration with a publishable key and a widget handoff into a deal.
Agent discovers offer → negotiates → signs → pays. Use this when your integration is agent-first and needs tooling, trust, and executable workflows.
The rest of this quickstart follows the server-side deal flow because it is the fastest path to a first successful integration.
Step 1: Create an API Key
Sign in to your Salesbooth account, navigate to Developers → API Keys, and create a new key.
Recommended scopes for a typical integration
Select only the scopes your integration needs. Start with these for a basic CPQ flow:
| Scope | Why you need it |
|---|---|
products:read | List and retrieve products for the widget |
deals:write | Create deals when customers complete checkout |
customers:write | Create or look up customer records |
webhooks:write | Register webhooks to receive deal lifecycle events |
Prefix your key with sb_test_ for sandbox mode. Sandbox deals don't charge real payment methods and can be reset at any time from Developers → Sandbox.
Your API key looks like this:
sb_test_au_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4Never expose sb_test_ or sb_live_ API keys in client-side JavaScript. Use those keys only server-side. For browser widgets, create a widget config and embed the returned sb_pub_ publishable key. That key has restricted widget scope: products:read, customers:write, deals:write, and agent:negotiate.
Step 2: Install the SDK
Choose your integration method:
<!-- Add to your HTML <head> -->
<script src="https://salesbooth.com/sdk/v1/salesbooth-widget.js"></script>npm install @salesbooth/node @salesbooth/sdk// ES Module
import { SalesBooth } from '@salesbooth/node';
const sb = new SalesBooth({ apiKey: 'sb_test_...' });pip install salesboothimport salesbooth
client = salesbooth.SalesBooth(api_key="sb_test_...")# No installation needed — use curl directly
export SB_API_KEY="sb_test_..."
export SB_BASE="https://api.salesbooth.com/v1"Step 3: Create Your First Product
Products are what your customers buy. Create one via the API or the Salesbooth dashboard.
curl -X POST https://api.salesbooth.com/v1/products \
-H "Authorization: Bearer $SB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Professional Plan",
"description": "Full-featured plan for growing teams",
"price": 99.00,
"price_type": "once_off"
}'# Response
{
"error": false,
"success": true,
"data": {
"product": {
"product_id": "prod_a1b2c3d4",
"version": 1,
"name": "Professional Plan",
"price": 99.00,
"price_type": "once_off",
"status": "active",
"created_at": "2026-03-09T10:30:00Z"
}
}
}const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_...' });
async function main() {
const product = await sb.products.create({
name: 'Professional Plan',
description: 'Full-featured plan for growing teams',
price: 99.00,
price_type: 'once_off'
});
console.log('Product created:', product.product_id); // prod_a1b2c3d4
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});import salesbooth
client = salesbooth.SalesBooth(api_key="sb_test_...")
product = client.products.create(
name="Professional Plan",
description="Full-featured plan for growing teams",
price=99.00,
price_type="once_off"
)
print("Product created:", product["product_id"]) # prod_a1b2c3d4You'll use the product ID (prod_a1b2c3d4) and a site ID (site_a1b2c3d4) to create a widget config in the next step.
Step 4: Create a Widget Config
Create a widget from Developers → Widget in the dashboard, or call POST /api/v1/widget-config. This step generates the browser-safe sb_pub_ publishable key used by the embed snippet.
POST /api/v1/widget-config requires an existing site_id. Before running the request below, create or select a site in the dashboard, then copy its site_id from Developers → Widget or Websites → Sites. The product ID from Step 3 is not enough on its own.
curl -X POST https://api.salesbooth.com/v1/widget-config \
-H "Authorization: Bearer $SB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"site_id": "site_a1b2c3d4",
"title": "Get Started Today",
"products": "prod_a1b2c3d4",
"currency": "USD",
"cta_text": "Complete Purchase"
}'# Response
{
"error": false,
"success": true,
"data": {
"widget_id": "wgt_a1b2c3d4",
"api_key": "sb_pub_au_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"api_key_id": "key_a1b2c3d4",
"products": "prod_a1b2c3d4",
"currency": "USD"
}
}const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_...' });
async function main() {
const widget = await sb.widgets.create({
site_id: 'site_a1b2c3d4',
title: 'Get Started Today',
products: 'prod_a1b2c3d4',
currency: 'USD',
cta_text: 'Complete Purchase'
});
console.log('Widget publishable key:', widget.api_key); // sb_pub_...
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});The embed key comes from data.api_key in the widget-config response. Do not reuse your secret sb_test_ or sb_live_ key in the widget.
Step 5: Embed the Deal Widget
The <salesbooth-deal> web component handles the entire deal flow — product selection, configuration, customer info, payment, and contract signature.
<!DOCTYPE html>
<html>
<head>
<script src="https://salesbooth.com/sdk/v1/salesbooth-widget.js"></script>
</head>
<body>
<salesbooth-deal
api-key="sb_pub_your_publishable_key"
products="prod_a1b2c3d4"
title="Get Started Today"
cta-text="Complete Purchase"
currency="USD"
></salesbooth-deal>
<script>
const widget = document.querySelector('salesbooth-deal');
widget.addEventListener('salesbooth:deal-created', (e) => {
console.log('Deal created!', e.detail);
});
</script>
</body>
</html>import { useEffect, useRef } from 'react';
import '@salesbooth/sdk/widget'; // registers <salesbooth-deal>
export function CheckoutWidget({ productId }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const handler = (e) => {
console.log('Deal created!', e.detail);
};
el.addEventListener('salesbooth:deal-created', handler);
return () => el.removeEventListener('salesbooth:deal-created', handler);
}, []);
return (
<salesbooth-deal
ref={ref}
api-key="sb_pub_your_key"
products={productId}
title="Get Started Today"
/>
);
}<template>
<salesbooth-deal
ref="widget"
api-key="sb_pub_your_key"
:products="productId"
title="Get Started Today"
/>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import '@salesbooth/sdk/widget';
const props = defineProps(['productId']);
const widget = ref(null);
function onDealCreated(e) {
console.log('Deal created!', e.detail);
}
onMounted(() => {
widget.value?.addEventListener('salesbooth:deal-created', onDealCreated);
});
onUnmounted(() => {
widget.value?.removeEventListener('salesbooth:deal-created', onDealCreated);
});
</script>Use your sb_pub_ publishable key in the widget — it's safe to expose in client-side code. It has restricted widget scope: products:read, customers:write, deals:write, and agent:negotiate. Keep your sb_test_ / sb_live_ keys server-side.
Step 6: Handle Deal Events
Listen for widget events to trigger your own workflows — send confirmation emails, update your CRM, provision access, etc.
Client-side events (widget)
const widget = document.querySelector('salesbooth-deal');
// Deal completed by customer
widget.addEventListener('salesbooth:deal-created', (e) => {
const { dealId, customerEmail, total } = e.detail;
console.log(`Deal ${dealId} created for ${customerEmail}, total: $${total}`);
// Redirect to thank-you page, trigger analytics, etc.
});
// Contract signed (contract accepted)
widget.addEventListener('salesbooth:contract-signed', (e) => {
provisionAccess(e.detail.contractId);
});
// Customer navigates to a new step
widget.addEventListener('salesbooth:step-change', (e) => {
analytics.track('checkout_step_change', { from: e.detail.from, to: e.detail.to });
});Server-side events (webhooks)
Configure a webhook endpoint in Developers → Webhooks to receive server-side notifications:
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/salesbooth', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-salesbooth-signature'];
const ts = req.headers['x-salesbooth-timestamp'];
const secret = process.env.WEBHOOK_SECRET;
// Verify signature
const signed = `${ts}.${req.body}`;
const expected = 'v1=' + crypto.createHmac('sha256', secret).update(signed).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
if (event.event === 'deal.created') {
console.log('New deal:', event.data.deal_id);
// Provision access, send email, etc.
}
res.json({ received: true });
});
app.listen(3000);import hmac, hashlib, time, os
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks/salesbooth', methods=['POST'])
def webhook():
sig = request.headers.get('X-Salesbooth-Signature', '')
ts = request.headers.get('X-Salesbooth-Timestamp', '')
secret = os.environ['WEBHOOK_SECRET']
body = request.get_data()
# Verify signature
signed = f"{ts}.{body.decode()}"
expected = 'v1=' + hmac.new(
secret.encode(), signed.encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
return jsonify({'error': 'Invalid signature'}), 401
event = request.get_json(force=True)
if event['event'] == 'deal.created':
print('New deal:', event['data']['deal_id'])
return jsonify({'received': True})<?php
$payload = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_SALESBOOTH_SIGNATURE'] ?? '';
$ts = $_SERVER['HTTP_X_SALESBOOTH_TIMESTAMP'] ?? '';
$secret = getenv('WEBHOOK_SECRET');
// Verify signature
$signed = $ts . '.' . $payload;
$expected = 'v1=' . hash_hmac('sha256', $signed, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
if ($event['event'] === 'deal.created') {
error_log('New deal: ' . $event['data']['deal_id']);
}
echo json_encode(['received' => true]);See the Webhook Integration Guide for full details on signature verification, retry logic, and event types.
Scope Selection Matrix
Choose the minimum scopes for your use case to follow the principle of least privilege.
Next Steps
You've created your first integration. Here's where to go next: