Salesbooth API
Deal infrastructure for AI commerce. Build integrations, automate deal flows, and embed commerce into any surface with our REST API.
Overview
The Salesbooth API is a RESTful JSON API. All requests and responses use application/json.
https://salesbooth.com/api/v1/Response Format
{
"error": false,
"success": true,
"data": { ... }
}{
"error": true,
"code": "VALIDATION_ERROR",
"category": "validation_error",
"message": "Name is required",
"recovery": {
"action": "fix_request",
"retryable": false
},
"ref": "abc123",
"details": { ... }
}Authentication
Authenticate requests with an API key sent as a Bearer token or via the X-API-Key header. All API keys must be sent in request headers — never in query parameters.
curl https://salesbooth.com/api/v1/deals \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/deals \
-H "X-API-Key: sb_test_example_key_do_not_use"Key Types
Salesbooth issues three distinct key types. Choosing the right type for each context is important for security.
Publishable keys and widgets: When you create a widget config via POST /api/v1/widget-config, the response includes a publishable_key (sb_pub_*). Pass this as the api-key attribute on the <salesbooth-deal> element. Never use a secret key (sb_live_* or sb_test_*) in client-side code.
Security Notice: Passing secret API keys (sb_live_*, sb_test_*) via the ?api_key= query parameter is deprecated and will be rejected in a future release. Query parameters appear in server logs, browser history, and referrer headers — use header-based authentication instead.
Tenants can opt in to immediate enforcement by setting enforce_header_auth to 1 in tenant settings.
Note: Publishable keys (sb_pub_*) used by widget public endpoints (/api/v1/widget-config, /api/v1/widget-intelligence, /api/v1/ab-tests/resolve) are not affected by this deprecation. These endpoints handle ?api_key= in their own code paths, independent of the standard authentication flow, and must continue to use query parameter auth for browser-embedded widgets.
CORS
API keys can be restricted to specific origins. Set allowed_origins when creating a key to limit which domains can use it from the browser.
Scopes & Permissions
API keys are scoped to specific resources and operations. Assign only the scopes your integration needs.
Rate Limiting
Salesbooth enforces a three-tier rate limiting system. Every request is checked against all three tiers simultaneously; the most restrictive limit that fires determines the response.
Tier 1 — Per-API-Key Limit
Each API key has a configurable per-minute limit (default: 1,000 req/min, sliding window). Agent keys receive a trust-level multiplier on top of the configured limit:
Multipliers apply to agent API keys only. Regular user keys always use the configured limit unchanged. In addition, each key gets a burst allowance of 25% of its per-minute limit, refilled at 1 token per second.
Tier 2 — Per-IP Limit
Tier 3 — Global Circuit Breaker
A platform-wide circuit breaker fires at 50,000 req/min across all tenants. When tripped, the API returns 503 Service Unavailable (not 429) with Retry-After. This protects all tenants during traffic spikes.
Response Headers
HTTP Status Codes
Retry Pattern
async function callWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const res = await fn();
if (res.status !== 429 && res.status !== 503) return res;
const retryAfter = parseInt(res.headers.get('Retry-After') || '5', 10);
const jitter = Math.random() * 1000;
await new Promise(r => setTimeout(r, retryAfter * 1000 + jitter));
}
throw new Error('Max retries exceeded');
}MCP endpoint rate limits: The POST /api/v1/mcp endpoint shares the same per-key limit. Each JSON-RPC call (including every tools/call invocation) counts as one request. Agents that issue many tools/call requests in a single workflow session can exhaust this quota quickly — implement backoff on 429 responses and consider batching where possible.
Error Handling
The API uses standard HTTP status codes. Every error response includes a machine-readable code, a human-readable message, and a recovery object with action, retryable, and a hint.
Error code format: The code field uses one of two formats depending on the error origin:
- Dotted lowercase (
category.specific_code) — used by all typed exception classes (e.g.conflict.stale_version,billing.trust_ceiling_exceeded,validation_error.invalid_fields). This is the preferred modern format. - Uppercase Deprecated (
SNAKE_CASE) — emitted by some legacy endpoint paths (e.g.VALIDATION_ERROR,BAD_REQUEST). These will be normalised to dotted lowercase format in a future release. Do not rely on exactcodestring matching for uppercase codes.
Migration guidance: Use the category field (always present and stable) for programmatic error handling instead of matching exact code strings. When the legacy uppercase codes are removed, integrations built on category will be unaffected. The stable category values are: validation_error, authentication_error, authorization_error, not_found, conflict, rate_limited, billing, transient_error, internal_error.
{
"error": true,
"code": "VALIDATION_ERROR",
"message": "Name is required",
"category": "validation_error",
"recovery": {
"action": "fix_request",
"retryable": false,
"hint": "Check the details.fields object for specific field errors."
},
"details": {
"fields": {
"name": { "code": "required", "message": "Name is required" }
}
}
}Standard HTTP Errors
Optimistic Locking (412)
Resources that support concurrent updates use optimistic locking. The current resource version is returned in the ETag response header. To update, send the version in an If-Match header. A version mismatch returns 412 Precondition Failed with the current resource state so you can merge and retry.
# First, fetch the current version
curl https://salesbooth.com/api/v1/deals?id=deal_abc123 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"
# Response includes: ETag: W/"5"
# Then update with the version
curl -X PATCH "https://salesbooth.com/api/v1/deals?id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"5\"" \
-d '{"title": "Updated Title"}'
# If another request updated the deal first, you get:
# 412 Precondition Failed
# { "error": true, "code": "conflict.stale_version", "category": "conflict",
# "message": "Resource has been modified by another request. Refresh and retry.",
# "recovery": { "action": "refetch_and_retry", "retryable": true,
# "hint": "Read the current resource version from response data, merge your changes, and retry with the new ETag." },
# "data": { <current resource state> } }Domain-Specific Error Codes
Retryable vs. Non-Retryable
Every error includes recovery.retryable. When true, the recovery.retry_after_seconds field tells you how long to wait before retrying. Implement exponential backoff for 503 and 429 responses.
Error Codes Reference
Complete taxonomy of all error codes returned by the API, grouped by category. Use the category field for programmatic handling — it is stable and will not change. The code field provides specific detail within a category.
Recommended pattern: Branch on category, not code. Future releases may add new code values within an existing category, but new categories will always be announced in the changelog.
Authentication Errors (401)
Authorization Errors (403)
Validation Errors (400)
Not Found (404)
Conflict Errors (409 / 412)
Billing Errors (402)
Rate Limiting (429)
Server & Transient Errors (500 / 503)
Error Handling — JavaScript (Fetch)
async function apiCall(path, options = {}) {
const res = await fetch(`https://salesbooth.com/api/v1/${path}`, {
...options,
headers: {
'Authorization': 'Bearer ' + API_KEY,
'Content-Type': 'application/json',
...(options.headers || {}),
},
});
const data = await res.json();
if (data.error) {
// Branch on category (stable) not code (may change)
switch (data.category) {
case 'authentication_error':
// Refresh or prompt for re-authentication
throw new Error('Authentication failed: ' + data.message);
case 'authorization_error':
throw new Error('Permission denied: ' + data.message);
case 'validation_error':
// data.details.fields has per-field errors
throw new Error('Validation failed: ' + data.message);
case 'not_found':
throw new Error('Resource not found: ' + data.message);
case 'conflict':
if (data.code === 'conflict.stale_version') {
// Merge data.data (current state) with your changes and retry
throw new Error('Version conflict — refetch and retry');
}
throw new Error('Conflict: ' + data.message);
case 'rate_limited':
// Retry after recovery.retry_after_seconds
const retryAfter = data.recovery?.retry_after_seconds || 60;
await new Promise(r => setTimeout(r, retryAfter * 1000));
return apiCall(path, options); // retry once
case 'transient_error':
// Short retry
await new Promise(r => setTimeout(r, (data.recovery?.retry_after_seconds || 5) * 1000));
return apiCall(path, options);
default:
throw new Error(data.message || 'An error occurred (ref: ' + data.ref + ')');
}
}
return data.data;
}Error Handling — curl
# All errors return a consistent JSON body:
curl "https://salesbooth.com/api/v1/deals?id=nonexistent" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"
# 404 response:
# {
# "error": true,
# "code": "not_found",
# "category": "not_found",
# "message": "Resource not found",
# "recovery": {
# "action": "check_resource_id",
# "retryable": false,
# "hint": "Verify the resource ID exists and you have access to it."
# },
# "ref": "trace_abc123" <-- include this in support requests
# }First Integration
The shortest path to a working integration. These four API calls are all you need to create a deal and collect a payment — no Intelligence API, negotiations, or webhooks required. Once this works, head to the SDK Quick Start for a more complete walkthrough.
Step 1 — Create a Product
curl -X POST https://salesbooth.com/api/v1/products \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Pro Plan",
"price": 99.00,
"type": "service"
}'
# Save the returned id: prod_xxxxxStep 2 — Create a Customer
curl -X POST https://salesbooth.com/api/v1/customers \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Smith",
"email": "jane@example.com",
"company": "Acme Corp"
}'
# Save the returned id: cust_xxxxxStep 3 — Create a Deal and Add a Line Item
# Create the deal
curl -X POST https://salesbooth.com/api/v1/deals \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_xxxxx",
"title": "Pro Plan — Jane Smith",
"currency": "USD",
"tax_rate": 0.10
}'
# Save the returned id: deal_xxxxx
# Add a line item
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=add_item" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"quantity": 1,
"unit_price": 99.00
}'
# Transition to in_progress so the customer can pay
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=transition&status=in_progress" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Step 4 — Collect Payment
curl -X POST https://salesbooth.com/api/v1/payments?action=create-intent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_xxxxx",
"amount": 108.90,
"currency": "USD"
}'
# Returns a Stripe client_secret — pass to Stripe.js on your frontend to complete paymentThat's it. You now have a complete deal flow: product → customer → deal → payment. For embeddable widgets, AI deal scoring, webhooks, negotiations, and more, continue to the SDK Quick Start below.
SDK Quick Start
From zero to a live deal widget in 5 minutes. This walkthrough installs the SDK, creates a product, creates a deal, and embeds the deal widget on your page.
Step 1 — Install
<script src="https://salesbooth.com/sdk/v1/salesbooth.js"></script># Install from npm
npm install @salesbooth/node
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });Step 2 — Create a Product
const product = await sb.products.create({
name: 'Pro Plan',
price: 99.00,
type: 'service',
description: 'Monthly pro subscription'
});
console.log('Product:', product.product_id); // prod_xxxxxStep 3 — Create a Customer & Deal
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
// Create a customer
const customer = await sb.customers.create({
name: 'Jane Smith',
email: 'jane@example.com',
company: 'Acme Corp'
});
// Create a deal with a line item
const deal = await sb.deals.create({
customer_id: customer.customer_id,
currency: 'USD',
tax_rate: 0.10
});
await sb.deals.addItem(deal.deal_id, {
product_id: 'prod_xxxxx',
quantity: 1,
unit_price: 499.00
});
console.log('Deal created:', deal.deal_id);SDK Resources: The SDK exposes sb.deals, sb.customers, sb.products, sb.contracts, sb.webhooks, sb.negotiations, sb.templates, sb.intelligence, sb.delegations, sb.configuration, sb.productRules, sb.savedConfigs, sb.widgets, and sb.analytics. Each resource provides list(), get(), create(), update(), and resource-specific methods.
Step 4 — Add a Line Item & Transition
await sb.deals.addItem(deal.deal_id, {
product_id: product.product_id,
quantity: 1,
unit_price: 99.00
});
// Move the deal from draft to in_progress
await sb.deals.transition(deal.deal_id, 'in_progress');
console.log('Deal ready:', deal.deal_id);Step 5 — Embed the Deal Widget
The deal widget lets buyers configure products, negotiate, and sign — all embedded in your site with a single script tag.
curl -X POST https://salesbooth.com/api/v1/widget-config \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"site_id": "site_xxxxx",
"title": "Get Pro Plan",
"currency": "USD",
"products": "prod_xxxxx"
}'
# Response includes: { "data": { "widget_id": "wgt_xxxxx", "publishable_key": "sb_pub_xxxxx" } }<!-- 1. Load the widget SDK -->
<script src="https://salesbooth.com/sdk/v1/salesbooth-widget.js"
crossorigin="anonymous"></script>
<!-- 2. Place the custom element anywhere in your page -->
<salesbooth-deal api-key="sb_pub_xxxxx"></salesbooth-deal>
<!-- 3. Listen for deal events -->
<script>
document.querySelector('salesbooth-deal').addEventListener('deal:closed', (e) => {
console.log('Deal closed!', e.detail.deal_id);
});
</script>Core integration complete. Steps 1–5 cover the most common use case. The steps below show optional advanced features — you can skip them on your first integration and return when you need them.
Step 6 — Score a Deal with the Intelligence API (optional)
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
// Get AI score for a deal
const score = await sb.intelligence.score('deal_abc123');
console.log(`Score: ${score.data.score}/100 (${score.data.confidence} confidence)`);
// Get pricing suggestions
const suggestion = await sb.intelligence.pricingSuggestion('deal_abc123');
console.log(`Suggested price: ${suggestion.data.suggested_price} (win rate: ${suggestion.data.win_rate_at_suggested})`);
// Get risk assessment
const risk = await sb.intelligence.risk('deal_abc123');
if (risk.data.overall_risk !== 'low') {
const highRisk = risk.data.factors.filter(f => f.severity === 'high');
highRisk.forEach(f => console.warn(`Risk: ${f.factor} — ${f.mitigation}`));
}
// Get pipeline forecast for next 90 days
const forecast = await sb.intelligence.pipelineForecast({ period: '90d' });
console.log(`90-day weighted pipeline: $${forecast.data.weighted_pipeline.toLocaleString()}`);Step 7 — Negotiate Deal Terms (optional)
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
// Propose terms to a merchant
await sb.negotiations.propose('deal_abc123', {
proposed_terms: { discount_percent: 12, payment_terms: 'net_30' },
message: 'Can we agree on 12% for a 3-year commitment?',
expires_at: '2026-03-19T00:00:00Z'
});
// Get AI suggestions before countering
const suggestions = await sb.negotiations.suggest('deal_abc123');
const balanced = suggestions.data.suggestions.find(s => s.strategy === 'balanced');
console.log('AI suggests:', balanced.proposed_terms, `(confidence: ${balanced.confidence})`);
// Counter with AI-suggested terms
await sb.negotiations.counter('deal_abc123', {
proposed_terms: balanced.proposed_terms,
message: balanced.rationale
});
// Accept the other party's latest terms
await sb.negotiations.accept('deal_abc123');
// Get full negotiation history
const history = await sb.negotiations.get('deal_abc123');
console.log(`${history.data.total_rounds} rounds, final status: ${history.data.status}`);Step 8 — Set Up a Webhook Listener (optional)
const crypto = require('crypto');
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
// Register a webhook endpoint
const webhook = await sb.webhooks.create({
url: 'https://myapp.com/webhooks/salesbooth',
events: ['deal.closed', 'deal.payment_received', 'subscription.renewed', 'negotiation.accepted'],
description: 'Production webhook'
});
console.log('Webhook registered:', webhook.webhook_id);
console.log('Signing secret (save this):', webhook.secret);
// Verify incoming events (server-side)
function verifyAndParseWebhook(rawBody, sig, timestamp, secret) {
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (age > 300) throw new Error('Stale event');
const expected = 'v1=' + crypto.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`, 'utf8').digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
throw new Error('Invalid signature');
return JSON.parse(rawBody);
}
// Handle deal.closed
function handleEvent(event) {
switch (event.event) {
case 'deal.closed':
console.log(`Deal ${event.data.deal_id} closed — revenue: ${event.data.total}`);
break;
case 'negotiation.accepted':
console.log(`Negotiation accepted: ${JSON.stringify(event.data.final_terms)}`);
break;
case 'subscription.renewed':
console.log(`Subscription renewed: $${event.data.total_amount} ${event.data.currency}`);
break;
}
}Step 9 — List Products and Create a Deal (optional)
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
// List available products
const products = await sb.products.list({ limit: 10 });
const product = products.products.find(p => p.name === 'Pro Plan');
console.log('Product:', product.product_id, '— price:', product.price);
// Create a customer
const customer = await sb.customers.create({
name: 'Jane Smith',
email: 'jane@example.com',
company: 'Acme Corp'
});
// Create deal, add item, and transition
const deal = await sb.deals.create({
customer_id: customer.customer_id,
currency: 'USD',
tax_rate: 0.10
});
await sb.deals.addItem(deal.deal_id, {
product_id: product.product_id,
quantity: 5,
unit_price: product.price
});
// Apply discount and transition
await sb.deals.applyDiscount(deal.deal_id, { type: 'percent', value: 10 });
await sb.deals.transition(deal.deal_id, 'in_progress');
console.log(`Deal ${deal.deal_id} ready — total: $${deal.total}`);Event Handling
sb.on('error', (err) => console.error(err.code, err.message));
sb.on('offline', () => console.log('Offline — requests will queue'));
sb.on('online', () => console.log('Back online — flushing queue'));Python Examples
The Salesbooth REST API is language-agnostic. The following examples use the requests library.
pip install requestsimport requests
API_KEY = "sb_test_example_key_do_not_use"
BASE = "https://salesbooth.com/api/v1"
HDR = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
# Create a customer
r = requests.post(f"{BASE}/customers", headers=HDR, json={
"name": "Jane Smith",
"email": "jane@example.com",
"company": "Acme Corp"
})
r.raise_for_status()
customer = r.json()["data"]
print("Customer:", customer["id"])
# Create a deal with idempotency
r = requests.post(f"{BASE}/deals", headers={**HDR, "Idempotency-Key": "order-20260317-001"}, json={
"customer_id": customer["id"],
"currency": "USD",
"tax_rate": 0.10
})
r.raise_for_status()
deal = r.json()["data"]
print("Deal:", deal["id"])
# Add a line item
r = requests.post(f"{BASE}/deals?id={deal['id']}&action=add_item", headers=HDR, json={
"product_id": "prod_xxxxx",
"quantity": 1,
"unit_price": 499.00
})
r.raise_for_status()
print("Line item added")
# Transition to in_progress
r = requests.post(f"{BASE}/deals?id={deal['id']}&action=transition&status=in_progress", headers=HDR)
r.raise_for_status()
print("Deal transitioned:", r.json()["data"]["status"])import hashlib, hmac, time
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_signing_secret"
TOLERANCE = 300 # 5 minutes
@app.post("/webhooks/salesbooth")
def handle_webhook():
sig = request.headers.get("X-Salesbooth-Signature", "")
timestamp = request.headers.get("X-Salesbooth-Timestamp", "0")
body = request.get_data() # raw bytes — do NOT parse before verifying
# Replay protection: reject events older than 5 minutes
if abs(time.time() - int(timestamp)) > TOLERANCE:
abort(400, "Stale webhook")
# HMAC-SHA256: sign timestamp.body
payload = f"{timestamp}.".encode() + body
expected = 'v1=' + hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest()
# Constant-time comparison
if not hmac.compare_digest(sig, expected):
abort(400, "Invalid signature")
event = request.get_json()
print(f"Event: {event['event']} id={event['id']}")
return "", 200curl Examples
curl "https://salesbooth.com/api/v1/deals?status=in_progress&limit=20" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -X POST https://salesbooth.com/api/v1/products \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise License",
"price": 4999.00,
"type": "service",
"description": "Annual enterprise subscription"
}'curl "https://salesbooth.com/api/v1/intelligence?deal_id=deal_abc123&type=score" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"What’s Next
Security Best Practices
Salesbooth handles payments, contracts, and customer PII. This section explains the security controls built into the API and the patterns you should follow in your integration.
Idempotency Keys
Any POST request that creates a financial record (deal, payment intent, subscription) accepts an optional Idempotency-Key header. If a request fails in transit or you receive no response, resend the request with the same key. The server will return the original response without double-creating the resource.
Use idempotency keys for: POST /deals, POST /payments, POST /subscriptions, POST /contracts. Keys must be unique per request, 1–64 characters, and should be regenerated for genuinely new operations.
curl -X POST https://salesbooth.com/api/v1/deals \
-H "Authorization: Bearer sb_live_xxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-03-17-acme-0042" \
-d '{ "customer_id": "cust_xxxxx", "currency": "USD" }'
# Network timeout — resend with the same key; no duplicate is created.
curl -X POST https://salesbooth.com/api/v1/deals \
-H "Authorization: Bearer sb_live_xxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-03-17-acme-0042" \
-d '{ "customer_id": "cust_xxxxx", "currency": "USD" }'
# Returns HTTP 200 (not 201) with the originally-created deal.Idempotency keys expire after 24 hours. Replies are cached so all responses, including errors, are returned as-is on a replay.
Field Whitelisting (Mass-Assignment Protection)
Every write endpoint accepts only the explicitly documented fields shown in its parameter table. Undocumented or system-controlled fields sent in the request body are silently dropped — they do not produce an error, and they are never persisted. This prevents mass-assignment attacks where a client might try to set server-managed values such as status, payment_status, tenant_id, or financial totals.
System-controlled fields that cannot be set by API clients:
id,tenant_id— generated by the serverstatus,payment_status,contract_status— controlled by state machine transitions, not free-form writessubtotal,total,tax_amount— calculated server-side from line itemscreated_at,updated_at,signed_at,closed_at— set by the server on lifecycle eventsversion,hash_chain— used for audit integrity; never writable
To change deal status, use the action=transition action endpoint, which enforces the state machine rules. Direct status writes are rejected.
Input Validation
All inputs are validated server-side before any database write occurs. Validation errors return 400 VALIDATION_ERROR with a details.fields map indicating which fields failed and why. Client-side validation should be considered a UX improvement only — never rely on it for security.
Distributed Transaction Semantics
Some Salesbooth operations touch multiple subsystems (deal, contract, payment, messaging). The platform uses a saga pattern with compensating transactions to handle partial failures:
- Atomic commitment — deal creation is a single database transaction. If any part fails, the whole operation rolls back and you receive an error response. No partial deal is created.
- Saga-based orchestration — operations that span subsystems (e.g. closing a deal triggers payment settlement, contract finalisation, and webhook delivery) use async jobs. If a downstream step fails, the system retries with exponential backoff up to 3 times. You can monitor job status via the
X-Job-IDresponse header on long-running operations. - Webhook delivery guarantees — webhooks are delivered at-least-once with a 3-attempt retry. Check the dead-letter queue (
GET /webhooks/dead_letter) to find undelivered events and usePOST /webhooks/replayto re-deliver them. - Idempotency on retries — use
Idempotency-Keyheaders so that retrying a failed request does not create duplicate records.
Data Encryption
All customer PII (names, emails, phone numbers) is encrypted at rest using per-tenant AES-256-GCM keys. Keys are rotated on a configurable schedule (default: 90 days). API responses return decrypted values to authorised callers; raw encrypted bytes are never exposed via the API.
PCI Compliance
Salesbooth is a PCI DSS Level 1 certified service provider. Card data is handled entirely by Stripe — raw card numbers never pass through Salesbooth servers. Salesbooth stores only Stripe PaymentIntent IDs and tokenised payment method references.
Your integration scope is reduced: Because card data flows directly from the buyer’s browser to Stripe’s hosted fields, your application only needs to satisfy SAQ A requirements (the simplest PCI assessment level) when using the Salesbooth widget. If you build a fully custom payment flow using the Salesbooth REST API directly, consult Stripe’s PCI guidance for your integration type.
Permission Boundaries
Different operations require different API key scopes, enforcing a principle of least privilege. A single key should be granted only the scopes it genuinely needs:
HTTPS and TLS
All API traffic must use HTTPS. HTTP requests are upgraded automatically via 301 redirect, but you should always use HTTPS in your integration from the start. The minimum TLS version accepted is TLS 1.2; TLS 1.3 is preferred. Certificate pinning is not required but your HTTP client should validate the certificate chain.
Deals
Create, manage, and transition deals through their lifecycle. Deals contain line items and support cryptographic signing and audit trails.
| Parameter | Description |
|---|---|
| id | Deal ID to retrieve a specific deal (e.g. deal_xxxxxxxxxx). When provided, returns a single deal object instead of a list. |
| action | Action to perform: terms (requires id), compare-terms (requires deal_id_1 & deal_id_2), audit, verify_audit, escrow, credential, valid_transitions, signing_certificate, export |
| deal_id_1 | First deal ID for compare-terms action |
| deal_id_2 | Second deal ID for compare-terms action |
| status | Filter: draft, in_progress, pending_signature, awaiting_signatures, pending_payment, partially_accepted, closed, cancelled, expired |
| customer_id | Filter by customer ID |
| created_after | ISO 8601 date filter |
| created_before | ISO 8601 date filter |
| min_value | Filter deals with total value >= this amount |
| max_value | Filter deals with total value <= this amount |
| limit | Max results, 1–100 (default: 50) |
| offset | Pagination offset (default: 0). Ignored when after/before cursors are provided. |
| after | Cursor for forward pagination. Pass the next_cursor from a previous response. |
| before | Cursor for backward pagination. Pass the prev_cursor from a previous response. |
| sort | Sort field for cursor pagination: created_at (default) or updated_at |
| fields | Comma-separated list of fields to include in the response (e.g. id,status,total) |
| include | Comma-separated nested resources to include (e.g. line_items,pricing,customer) |
| exclude | Comma-separated nested resources to exclude from the response |
| format | Response format shortcut: minimal returns only id, status, and updated_at |
| Field | Type | Description |
|---|---|---|
| title required | string | Deal title (max 255 chars) |
| customer_id | string | Customer identifier (omit for widget negotiations or draft deals) |
| description | string | Deal description |
| currency | string | ISO 4217 currency code (default: USD) |
| tax_rate | number | Tax rate as decimal (e.g. 0.10 for 10%) |
| deal_type | string | one_time (default) or recurring |
| billing_cycle | string | monthly, quarterly, or annual (required when deal_type is recurring) |
| presentation_currency | string | ISO 4217 currency for display (auto-converted from currency) |
| settlement_currency | string | ISO 4217 currency for settlement |
| status | string | Initial status (default: draft) |
| notes | string | Internal notes (not visible to customers) |
| metadata | object | Arbitrary key-value metadata (max depth 3) |
| template_id | string | Deal template ID to create from (pre-populates fields from template) |
curl -X POST https://salesbooth.com/api/v1/deals \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_xxxxx",
"title": "Enterprise License Deal",
"currency": "USD",
"tax_rate": 0.10
}'{
"error": false,
"data": {
"deal_id": "deal_abc123",
"version": 1,
"customer_id": "cust_xxxxx",
"title": "Enterprise License Deal",
"status": "draft",
"currency": "USD",
"line_items": [],
"pricing": {
"currency": "USD",
"tax_rate": "0.1000",
"subtotal": { "amount": "0.00", "currency": "USD", "formatted": "$0.00" },
"total": { "amount": "0.00", "currency": "USD", "formatted": "$0.00" }
},
"created_at": "2026-03-09T10:30:00Z",
"updated_at": "2026-03-09T10:30:00Z"
}
}If-Match header with the current ETag version for optimistic locking.| Field | Type | Description |
|---|---|---|
| customer_id | string | Customer ID to reassign this deal to |
| title | string | Deal title |
| description | string | Deal description |
| notes | string | Internal notes |
| status | string | draft, in_progress, pending_payment, pending_signature, partially_accepted, closed, cancelled, or expired |
| currency | string | ISO 4217 currency code (e.g. USD) |
| tax_rate | number | Tax rate as decimal (e.g. 0.10 for 10%). Values ≥1 are automatically divided by 100 for compatibility (e.g. 10 → 0.10); prefer the decimal form. |
| deal_type | string | one_time or recurring |
| billing_cycle | string | monthly, quarterly, or annual |
| metadata | object | Arbitrary key-value metadata (max depth 3) |
curl -X PATCH "https://salesbooth.com/api/v1/deals?id=deal_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: 3" \
-d '{
"currency": "AUD",
"tax_rate": 0.10,
"notes": "Updated pricing agreed in call"
}'cancelled status). If the deal is already cancelled, it is hard-deleted. Pass action=remove_item&item_id={id} to remove a single line item instead.curl -X DELETE "https://salesbooth.com/api/v1/deals?id=deal_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: 1"curl -X DELETE "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=remove_item&item_id=item_yyyyy" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: 1"Deal Actions
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal ID |
| product_id required | string | Product to add |
| quantity | integer | Quantity (default 1) |
| options | object | Selected configuration options |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=add_item" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_xxxxx",
"product_id": "prod_xxxxx",
"quantity": 2,
"options": {}
}'curl "https://salesbooth.com/api/v1/deals/deal_xxxxx/valid-transitions" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"deal_id": "deal_xxxxx",
"current_status": "in_progress",
"transitions": [
{
"target": "pending_signature",
"available": true,
"conditions": [],
"action": {
"method": "POST",
"endpoint": "/api/v1/deals?id=deal_xxxxx&action=transition&status=pending_signature",
"description": "Send for signature"
}
},
{
"target": "cancelled",
"available": true,
"conditions": [],
"action": {
"method": "POST",
"endpoint": "/api/v1/deals?id=deal_xxxxx&action=transition&status=cancelled",
"description": "Cancel"
}
}
]
}
}draft → in_progress → pending_signature → pending_payment → closed.| Field | Type | Description |
|---|---|---|
| signer_type | string | user, customer, agent, or api_key |
| signer_id | string | ID of the signer (optional; defaults to the authenticated identity) |
| signer_customer_id | string | Customer ID to sign on behalf of. Required when an API key signs a deal that belongs to a specific customer. Triggers authorization checks via the deal's customer association. |
| consent_to_sign | boolean | Required true for human signers (user or customer) |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=sign" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"consent_to_sign": true}'| Field | Type | Description |
|---|---|---|
| reason | string | Optional reason for removing the signature (recorded in audit log) |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=unsign" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "Terms need revision"}'item_id query parameter is required.| Field | Type | Description |
|---|---|---|
| quantity required | integer | New quantity (minimum: 1) |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=update_item&item_id=item_yyyyy" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"quantity": 3}'| Field | Type | Description |
|---|---|---|
| type required | string | percentage or fixed |
| value required | number | Discount value — percentage points (e.g. 10 for 10% off) or fixed currency amount |
| code | string | Optional promo code associated with this discount |
| description | string | Human-readable description of the discount (shown on invoice) |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=apply_discount" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"type": "percentage",
"value": 15,
"description": "Loyalty discount"
}'curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=apply_discount" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"type": "fixed",
"value": 50,
"code": "SAVE50",
"description": "Promotional offer"
}'| Field | Type | Description |
|---|---|---|
| terms required | object | Deal terms object. Must include terms_schema_version and payment_terms. See Deal Terms Schema for the full field reference. |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=set-terms" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"terms": {
"terms_schema_version": "1.0",
"payment_terms": {
"type": "net",
"due_date": "2026-04-30"
}
}
}'agent:execute API key scope.| Field | Type | Description |
|---|---|---|
| customer_id | string | Existing customer ID. Provide either customer_id or customer (inline create) |
| customer | object | Inline customer: name, email, phone, company. Creates the customer if they don't exist (matched by email) |
| items | array | Line items array. Each item: product_id, quantity, unit_price, name, optional configuration with option_ids |
| saved_config_id | string | Saved product configuration ID. Use instead of items to populate line items from a saved configuration |
| discounts | array | Optional discounts array. Each entry: type (percentage or fixed), value, description |
| template_id | string | Optional deal template to apply default settings from |
| currency | string | ISO 4217 currency code (e.g. USD) |
| notes | string | Internal notes |
| metadata | object | Arbitrary key-value metadata |
| dry_run | boolean | If true, validates and returns the deal object without persisting it |
curl -X POST "https://salesbooth.com/api/v1/deals?action=create-configured" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"name": "Jane Smith",
"email": "jane@example.com",
"company": "Acme Corp"
},
"items": [
{
"product_id": "prod_xxxxx",
"name": "Professional Plan",
"quantity": 1,
"unit_price": 499.00
}
],
"discounts": [
{"type": "percentage", "value": 10, "description": "First-year discount"}
],
"currency": "USD"
}'partially_accepted status. Optionally creates a separate deal for the rejected items.| Field | Type | Description |
|---|---|---|
| accepted_items required | array | Array of line item IDs to accept (at least one required) |
| rejected_items | array | Array of line item IDs being explicitly rejected (informational) |
| create_declined_deal | boolean | If true, create a new deal containing the rejected items for further negotiation |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=partial_accept" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"accepted_items": ["item_aaa", "item_bbb"],
"rejected_items": ["item_ccc"],
"create_declined_deal": true
}'release_escrow) or the escrow expires/is refunded. Escrow expires after 7 days by default.| Field | Type | Description |
|---|---|---|
| conditions required | array | Array of condition objects, each with type (e.g. delivery, approval) and optional config |
| amount | number | Amount to hold in escrow. Defaults to the deal total |
| currency | string | ISO 4217 currency code. Defaults to the deal currency |
| expires_in | integer | Seconds until the escrow expires (default: 604800 / 7 days) |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=escrow" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"conditions": [
{"type": "delivery", "config": {"tracking_required": true}},
{"type": "approval"}
],
"expires_in": 1209600
}'escrow.released webhook event and closes the deal.curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=release_escrow" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"escrow.refunded webhook event.| Field | Type | Description |
|---|---|---|
| reason | string | Reason for refund: manual (default), condition_failed, deal_cancelled, fraud, or duplicate |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=refund_escrow" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "condition_failed"}'escrow.condition_fulfilled webhook event.| Field | Type | Description |
|---|---|---|
| condition_id required | string | ID of the escrow condition to mark as fulfilled |
| met_by | string | Optional identifier of the party or system that fulfilled the condition |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=fulfill_condition" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"condition_id": "cond_yyyyy",
"met_by": "logistics_system"
}'| Field | Type | Description |
|---|---|---|
| reason | string | Reason for revocation: manual (default), deal_cancelled, fraud, expired, or superseded |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=revoke_credential" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "superseded"}'| Field | Type | Description |
|---|---|---|
| reason required | string | Outcome reason. Typically won, lost, no_decision, or a custom value |
| notes | string | Free-text notes about the outcome (stored in audit log) |
| competitor_name | string | Name of the competitor chosen instead (for loss analysis) |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=record-outcome" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "won", "notes": "Closed after demo on 2026-03-28"}'curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=record-outcome" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"reason": "lost",
"notes": "Went with competitor on price",
"competitor_name": "CompetitorCo"
}'template_id on a create request.| Field | Type | Description |
|---|---|---|
| name required | string | Template name (1–255 characters) |
| description | string | Optional description shown in the template picker |
curl -X POST "https://salesbooth.com/api/v1/deals?id=deal_xxxxx&action=save-as-template" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise Annual Plan",
"description": "Standard enterprise deal with annual billing and 10% loyalty discount"
}'curl "https://salesbooth.com/api/v1/deals/deal_xxxxx/settlements" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"settlements": [
{
"participant_id": "part_abc",
"role": "seller",
"share_percent": 85.00,
"amount": 850.00,
"currency": "USD",
"status": "settled"
},
{
"participant_id": "part_def",
"role": "referrer",
"share_percent": 15.00,
"amount": 150.00,
"currency": "USD",
"status": "settled"
}
]
}
}| Parameter | Type | Description |
|---|---|---|
| id | string | Deal ID to verify (provide either id or contract_id) |
| contract_id | string | Contract ID to verify |
curl "https://salesbooth.com/api/v1/deals/verify?id=deal_abc123"{
"error": false,
"data": {
"verification": {
"deal_id": "deal_abc123",
"status": "verified",
"message": "Deal signature is valid and data is unmodified",
"deal_hash": {
"stored": "a3f2b1c9...",
"current": "a3f2b1c9...",
"matches": true
},
"signature": {
"valid": true,
"signature_algorithm": "ed25519",
"signed_at": "2026-03-10T14:22:00Z"
},
"verification_method": "public_key",
"verification_note": "This signature can be independently verified using the public key without trusting Salesbooth infrastructure."
}
}
}Possible status values: verified (deal matches signed state), tampered (deal was modified after signing), unsigned (no signature exists).
Customers
Manage customer records. Sensitive PII (name, email, phone) is encrypted at rest with searchable blind indexes.
| Parameter | Description |
|---|---|
| id | Customer ID to retrieve a specific customer (e.g. cust_xxxxxxxxxx). When provided, returns a single customer object instead of a list. |
| status | Filter: active, inactive, archived |
| search | Search name, email, or phone |
| limit | Max results, 1–100 (default: 50) |
| offset | Pagination offset (default: 0). Ignored when after/before cursors are provided. |
| after | Cursor for forward pagination. Pass the next_cursor from a previous response. |
| before | Cursor for backward pagination. Pass the prev_cursor from a previous response. |
| sort | Sort field for cursor pagination: created_at (default) or updated_at |
| fields | Comma-separated list of fields to include in the response (e.g. id,status,name) |
| include | Comma-separated nested resources to include (e.g. deals,contracts,activity_log) |
| exclude | Comma-separated nested resources to exclude from the response |
| format | Response format shortcut: minimal returns only id, status, and updated_at |
curl -X POST https://salesbooth.com/api/v1/customers \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+61412345678",
"company": "Acme Corp",
"status": "active"
}'| Field | Description |
|---|---|
| name required | Customer full name |
| email required | Email address |
| phone | Phone number (E.164 format) |
| company | Company or organisation name |
| city | City |
| state | State or region |
| zip | Postal code |
| country | Country code (e.g. AU) |
| address | Street address |
| notes | Internal notes |
| status | Customer status: active (default), inactive, archived |
If-Match header with the current ETag version for optimistic locking.| Field | Type | Description |
|---|---|---|
| name | string | Full name |
| string | Email address (must be unique per tenant) | |
| phone | string | Phone number (normalised to E.164) |
| address | string | Street address |
| company | string | Company name |
| city | string | City |
| state | string | State or province |
| zip | string | Postal code |
| country | string | Country |
| notes | string | Internal notes |
| status | string | active, inactive, or archived |
curl -X PATCH "https://salesbooth.com/api/v1/customers?id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: 5" \
-d '{
"phone": "+14155552671",
"company": "Acme Corp (updated)",
"status": "active"
}'curl -X DELETE "https://salesbooth.com/api/v1/customers?id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: 1"Products
Manage your product catalogue. Products can be physical goods or services and are referenced by deal line items.
| Parameter | Description |
|---|---|
| id | Product ID to retrieve a specific product (e.g. prod_xxxxxxxxxx). When provided, returns a single product object instead of a list. |
| action | Sub-action to perform: options (requires id) — returns the product’s configuration options schema |
| status | Filter: active, inactive, archived |
| type | Filter: product, service |
| category | Filter by category name |
| family | Filter by product family ID |
| requires_booking | Filter to bookable service products: 1 to include only bookable products, 0 to exclude them |
| search | Search name, SKU, or description |
| limit | Max results, 1–100 (default: 50) |
| offset | Pagination offset (default: 0). Ignored when after/before cursors are provided. |
| after | Cursor for forward pagination. Pass the next_cursor from a previous response. |
| before | Cursor for backward pagination. Pass the prev_cursor from a previous response. |
| sort | Sort field for cursor pagination: created_at (default) or updated_at |
| fields | Comma-separated list of fields to include in the response (e.g. id,status,name,price) |
| include | Comma-separated nested resources to include (e.g. deals,activity_log) |
| exclude | Comma-separated nested resources to exclude from the response |
| format | Response format shortcut: minimal returns only id, status, and updated_at |
curl -X POST https://salesbooth.com/api/v1/products \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise License",
"price": 999.00,
"type": "service",
"description": "Annual enterprise license",
"sku": "ENT-001"
}'| Field | Type | Description |
|---|---|---|
| name required | string | Product name |
| price required | number | Selling price |
| description | string | Product description |
| sku | string | Stock keeping unit |
| type | string | product, service, subscription, or bundle |
| price_type | string | once_off, recurring, percentage, or metered |
| billing_cycle | string | monthly, quarterly, or annual — required when price_type is recurring |
| pricing_model | string | fixed, tiered, volume, or usage |
| cost | number | Cost price (for margin calculation) |
| unit | string | Unit label (e.g. “per seat”, “per month”) |
| tax_rate | number | Tax rate as decimal (e.g. 0.10 for 10%) |
| category | string | Product category |
| status | string | active, inactive, or archived (default: active) |
| stock_quantity | integer | Available stock quantity |
| track_inventory | boolean | Enable inventory tracking for this product |
| low_stock_threshold | integer | Stock level at which low-stock alerts are triggered |
| metadata | object | Arbitrary key-value metadata |
| configuration_schema | object | JSON schema defining configurable options for this product |
| family_id | string | Product family ID to associate this product with |
| requires_booking | boolean | Whether this product requires a booking (default: false) |
| session_duration | integer | Session length in minutes (5–480) |
| buffer_time | integer | Buffer time between sessions in minutes (0–120) |
| max_advance_days | integer | Maximum days in advance a booking can be made (default: 90) |
| min_advance_hours | integer | Minimum hours lead time required for a booking (default: 24) |
| allow_staff_selection | boolean | Whether customers can choose their preferred staff member (default: true) |
| learn_more_url | string | URL linking to more information about the product |
| features | array | List of product feature strings |
If-Match header with the current ETag version for optimistic locking.| Field | Type | Description |
|---|---|---|
| name | string | Product name |
| description | string | Product description |
| price | number | Unit price |
| cost | number | Cost price (for margin calculation) |
| sku | string | Stock keeping unit |
| type | string | product, service, subscription, or bundle |
| unit | string | Unit label (e.g. “per seat”) |
| tax_rate | number | Tax rate as decimal (e.g. 0.10 for 10%) |
| category | string | Product category |
| status | string | active, inactive, or archived |
| stock_quantity | integer | Available stock quantity |
| track_inventory | boolean | Enable inventory tracking |
| low_stock_threshold | integer | Stock level at which low-stock alerts are triggered |
| price_type | string | once_off, recurring, percentage, or metered |
| billing_cycle | string | monthly, quarterly, or annual — required when price_type is recurring |
| pricing_model | string | fixed, tiered, volume, or usage |
| metadata | object | Arbitrary key-value metadata |
| configuration_schema | object | JSON schema defining configurable options for this product |
| family_id | string | Product family ID to associate this product with |
| requires_booking | boolean | Whether this product requires a booking |
| session_duration | integer | Session length in minutes (5–480) |
| buffer_time | integer | Buffer time between sessions in minutes (0–120) |
| max_advance_days | integer | Maximum days in advance a booking can be made |
| min_advance_hours | integer | Minimum hours lead time required for a booking |
| allow_staff_selection | boolean | Whether customers can choose their preferred staff member |
| learn_more_url | string | URL linking to more information about the product |
| features | array | List of product feature strings |
curl -X PATCH "https://salesbooth.com/api/v1/products?id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: 2" \
-d '{
"price": 1199.00,
"status": "active",
"stock_quantity": 50
}'curl -X DELETE "https://salesbooth.com/api/v1/products?id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: 1"Contracts
Create and manage contracts with lifecycle transitions, cryptographic signing, and immutable audit trails.
| Parameter | Description |
|---|---|
| id | Contract ID to retrieve a specific contract. When provided, returns a single contract object instead of a list. |
| action | Action to perform (requires id): audit, verify_audit, signing_certificate |
| status | Filter: draft, signed, active, expired, terminated |
| customer_id | Filter by customer |
| renewal_type | Filter by renewal type: manual, auto, none |
| expiring_soon | Filter contracts expiring within N days (e.g. 30 for contracts expiring in the next 30 days) |
| limit | Max results, 1–100 (default: 50) |
| offset | Pagination offset (default: 0). Ignored when after/before cursors are provided. |
| after | Cursor for forward pagination. Pass the next_cursor from a previous response. |
| before | Cursor for backward pagination. Pass the prev_cursor from a previous response. |
| sort | Sort field for cursor pagination: created_at (default) or updated_at |
| fields | Comma-separated list of fields to include in the response (e.g. contract_id,status,title,value) |
| include | Comma-separated nested resources to include (e.g. activity_log) |
| exclude | Comma-separated nested resources to exclude from the response |
| format | Response format shortcut: minimal returns only contract_id, status, and updated_at |
deal_id (which auto-populates customer, value, dates, and currency).curl -X POST https://salesbooth.com/api/v1/contracts \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_xxxxx",
"payment_terms": "net30",
"renewal_type": "auto"
}'curl -X POST https://salesbooth.com/api/v1/contracts \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_xxxxx",
"title": "Annual Support Agreement",
"value": 12000.00,
"currency": "AUD",
"start_date": "2026-04-01",
"end_date": "2027-03-31",
"payment_terms": "net30",
"renewal_type": "auto"
}'| Field | Description |
|---|---|
| deal_id | Create a contract from an existing deal. Auto-populates customer_id, title, value, currency, start_date, and end_date from the deal. Any of those fields may still be provided to override the deal-derived values. Required unless the five core fields are supplied. |
| customer_id required* | Customer to attach the contract to. Required unless deal_id is provided. |
| title required* | Contract title. Required unless deal_id is provided. |
| value required* | Contract value (numeric, ≥ 0). Required unless deal_id is provided. |
| start_date required* | Contract start date (YYYY-MM-DD). Required unless deal_id is provided. |
| end_date required* | Contract end date (YYYY-MM-DD, must be after start_date). Required unless deal_id is provided. |
| contract_number | Custom contract reference number (e.g. CTR-2026-001). |
| description | Contract description. |
| currency | ISO 4217 currency code (default: USD). |
| payment_terms | Payment terms text (e.g. net30). |
| renewal_type | Renewal configuration: auto, manual, or none (default: manual). |
| renewal_terms | Structured renewal terms object (JSON). Stores renewal period, notice window, and other renewal-specific configuration. |
| notes | Internal notes (not visible to the customer). |
* Required unless deal_id is provided.
If-Match header with the current ETag for optimistic locking.| Field | Description |
|---|---|
| title | Contract title |
| description | Contract description. |
| value | Contract value (numeric, ≥ 0) |
| currency | ISO 4217 currency code |
| start_date | Contract start date (YYYY-MM-DD) |
| end_date | Contract end date (YYYY-MM-DD, must be after start_date) |
| renewal_type | Renewal configuration: auto, manual, or none |
| renewal_terms | Structured renewal terms object (JSON). Stores renewal period, notice window, and other renewal-specific configuration. |
| payment_terms | Payment terms text (e.g. net30) |
| notes | Internal notes (not visible to the customer). |
| status | Contract status: draft, signed, active, expired, terminated |
| customer_id | Reassign the contract to a different customer. |
| contract_number | Custom contract reference number (e.g. CTR-2026-001). |
curl -X PATCH "https://salesbooth.com/api/v1/contracts?id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: 1" \
-d '{
"title": "Annual SaaS Agreement (Revised)",
"payment_terms": "net30",
"end_date": "2027-01-31"
}'terminate action). Requires an If-Match header with the current ETag.curl -X DELETE "https://salesbooth.com/api/v1/contracts?id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: 2"Contract Actions
Payments
Manage the payment lifecycle for deals via Stripe PaymentIntents. Create payment intents, confirm payments, issue refunds, and record manual payments.
| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal identifier |
curl https://salesbooth.com/api/v1/payments?deal_id=deal_abc123 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/payments?action=settings \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal to collect payment for |
| amount | number | Amount in major currency units (defaults to deal total) |
| currency | string | ISO 4217 currency code (defaults to deal currency) |
curl -X POST https://salesbooth.com/api/v1/payments?action=create-intent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"amount": 1099.00,
"currency": "USD"
}'{
"error": false,
"data": {
"payment_intent_id": "pi_xxxxx",
"client_secret": "pi_xxxxx_secret_xxxxx",
"amount": 1099.00,
"currency": "usd",
"status": "requires_payment_method"
}
}| Field | Type | Description |
|---|---|---|
| payment_intent_id required | string | The Stripe PaymentIntent ID |
curl -X POST https://salesbooth.com/api/v1/payments?action=confirm \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "payment_intent_id": "pi_xxxxx" }'| Field | Type | Description |
|---|---|---|
| payment_intent_id required | string | The Stripe PaymentIntent ID to refund |
| amount | number | Partial refund amount (omit for full refund) |
| reason | string | Refund reason |
curl -X POST https://salesbooth.com/api/v1/payments?action=refund \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"payment_intent_id": "pi_xxxxx",
"amount": 200.00,
"reason": "Partial refund for returned item"
}'| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal identifier |
| amount required | number | Payment amount |
| currency | string | Currency code |
curl -X POST https://salesbooth.com/api/v1/payments?action=record-manual \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "amount": 5000.00 }'Subscriptions
Create and manage recurring subscriptions built on top of deals. Supports billing cycles, cancellation, pausing, resuming, cycle changes, metered billing, and renewal tracking.
Subscription Lifecycle
┌───────────────────────────────────────────────────────────────────────────┐
│ SUBSCRIPTION STATE MACHINE │
│ │
│ (closed deal) │
│ │ │
│ │ action=create │
│ ▼ │
│ ┌─────────┐ action=pause ┌────────┐ action=cancel ┌───────────┐ │
│ │ active │────────────────▶│ paused │─────────────────▶│ cancelled │ │
│ └─────────┘ └────────┘ └───────────┘ │
│ │ ▲ │ │
│ │ │ action=resume │ action=resume │
│ │ └───────────────────────┘ │
│ │ │
│ │ payment fails at renewal │
│ ▼ │
│ ┌──────────┐ action=retry-payment ┌──────────┐ │
│ │ past_due │───────────────────────▶│ active │ │
│ └──────────┘ (if payment succeeds) └──────────┘ │
│ │ │
│ │ grace period expires │
│ ▼ │
│ ┌───────────┐ │
│ │ suspended │ │
│ └───────────┘ │
└───────────────────────────────────────────────────────────────────────────┘action=analytics for subscription metrics.| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific subscription by deal ID |
| status | string | Filter: active, paused, cancelled, past_due |
| action | string | analytics for subscription metrics; usage (with id) for metered usage records |
curl "https://salesbooth.com/api/v1/subscriptions?status=active" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/subscriptions?id=deal_abc123&action=usage_summary" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal to subscribe |
| action required | string | create |
| billing_cycle | string | monthly (default), quarterly, or annual |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"action": "create",
"billing_cycle": "monthly"
}'curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "action": "pause" }'curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "action": "resume" }'| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | cancel |
| end_of_period | boolean | If false, cancels immediately (default: true) |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "action": "cancel", "end_of_period": true }'curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "action": "renew" }'past_due subscription. Optionally extends the grace period.| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | retry-payment |
| grace_days | integer | Additional days to extend the grace period (optional) |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "action": "retry-payment", "grace_days": 3 }'| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | change |
| line_items required | array | New line items: product_id, quantity, unit_price |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"action": "change",
"line_items": [
{ "product_id": "prod_enterprise", "quantity": 1, "unit_price": 299.00 }
]
}'curl "https://salesbooth.com/api/v1/subscriptions?id=deal_abc123&action=usage_summary" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"deal_id": "deal_abc123",
"cycle_start": "2026-03-01T00:00:00Z",
"cycle_end": "2026-04-01T00:00:00Z",
"meters": [
{
"metric_name": "api_calls",
"units_used": 12547,
"included_units": 10000,
"overage_units": 2547,
"unit_price": 0.001,
"overage_charge": 2.55,
"currency": "USD"
}
],
"base_amount": 99.00,
"metered_amount": 2.55,
"estimated_total": 101.55,
"currency": "USD"
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | change-cycle |
| billing_cycle required | string | monthly, quarterly, or annual |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "action": "change-cycle", "billing_cycle": "annual" }'Metered Billing
Attach usage meters to subscriptions to bill based on consumption (e.g. API calls, seats, storage). Meters accumulate usage and are billed at renewal.
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | add-meter |
| meter_name required | string | Meter identifier (e.g. api_calls, seats, storage_gb) |
| unit_label | string | Display label (e.g. API Call, Seat) |
| price_per_unit required | number | Price charged per unit of usage |
| free_units | integer | Included free units per billing period (default: 0) |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"action": "add-meter",
"meter_name": "api_calls",
"unit_label": "API Call",
"price_per_unit": 0.001,
"free_units": 10000
}'| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | remove-meter |
| meter_id required | string | ID of the meter to remove |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"action": "remove-meter",
"meter_id": "meter_xyz789"
}'| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | record-usage |
| meter_name required | string | Meter to record against (must exist on the subscription) |
| quantity required | number | Units consumed in this event |
| idempotency_key | string | Optional idempotency key to prevent duplicate recording |
curl -X POST https://salesbooth.com/api/v1/subscriptions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"action": "record-usage",
"meter_name": "api_calls",
"quantity": 150,
"idempotency_key": "usage-2026-03-12-batch-7"
}'subscription.meter_added — meter attached to subscription
subscription.renewed — renewal processed, metered charges included
subscription.past_due — payment failed at renewalStaff
Manage staff members — service providers who can be assigned to bookable products and scheduled for availability.
Required Scopes
| staff:read | Required for listing and retrieving staff members |
| staff:write | Required for creating, updating, deactivating staff, and managing schedules |
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific staff member with schedule and assigned products |
| status | string | Filter by status: active, inactive |
| product_id | string | Filter by assigned product |
| limit | integer | Max results to return (default: 50) |
| offset | integer | Pagination offset |
curl https://salesbooth.com/api/v1/staff \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| display_name required | string | Full display name of the staff member |
| title | string | Job title or role (e.g. Senior Consultant) |
| bio | string | Short biography shown to customers |
| avatar_url | string | URL of the staff member’s profile photo |
| user_id | string | Linked tenant user account ID |
| status | string | Account status: active or inactive |
| max_daily_sessions | integer | Maximum bookings per day (1–100) |
| default_session_duration | integer | Default booking duration in minutes (5–480) |
| buffer_time | integer | Buffer between bookings in minutes (0–240) |
| timezone | string | Staff member’s timezone (e.g. America/New_York) |
| metadata | object | Arbitrary key/value metadata |
| schedule | array | Weekly availability schedule (up to 7 entries) |
| product_ids | array | Product IDs this staff member is qualified to deliver |
curl -X POST https://salesbooth.com/api/v1/staff \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Jane Smith",
"title": "Senior Consultant",
"timezone": "America/New_York"
}'curl -X PATCH "https://salesbooth.com/api/v1/staff?id=staff_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "title": "Lead Consultant" }'curl -X DELETE "https://salesbooth.com/api/v1/staff?id=staff_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Schedule Management
| Field | Type | Description |
|---|---|---|
| schedule required | array | Array of daily schedule objects with day_of_week (0–6), start_time, end_time |
curl -X POST "https://salesbooth.com/api/v1/staff?id=staff_xxxxx&action=schedule" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"schedule": [
{ "day_of_week": 1, "start_time": "09:00", "end_time": "17:00" },
{ "day_of_week": 2, "start_time": "09:00", "end_time": "17:00" }
]
}'| Field | Type | Description |
|---|---|---|
| product_id required | string | The product to assign the staff member to |
curl -X POST "https://salesbooth.com/api/v1/staff?id=staff_xxxxx&action=assign_product" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "product_id": "prod_xxxxx" }'Bookings
Manage service bookings — hold time slots, confirm appointments, track attendance, and handle cancellations.
Required Scopes
| bookings:read | Required for listing and retrieving bookings |
| bookings:write | Required for creating, updating, and cancelling bookings |
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific booking |
| status | string | Filter: held, confirmed, completed, cancelled, no_show |
| product_id | string | Filter by product |
| staff_id | string | Filter by staff member |
| deal_id | string | Filter by associated deal |
| date_from | string (date) | Start date filter (YYYY-MM-DD) |
| date_to | string (date) | End date filter (YYYY-MM-DD) |
| action | string | analytics for booking metrics |
| period | string | Analytics period (e.g. 7d, 30d, 90d; default: 30d). Used with action=analytics |
| limit | integer | Max results (default: 50) |
| offset | integer | Pagination offset |
curl https://salesbooth.com/api/v1/bookings?status=confirmed \
-H "Authorization: Bearer sb_test_example_key_do_not_use"held to temporarily reserve a slot during checkout.| Field | Type | Description |
|---|---|---|
| product_id required | string | The bookable product |
| staff_id required | string | Staff member to assign the booking to |
| date required | string (date) | Appointment date (YYYY-MM-DD) |
| start_time required | string (time) | Appointment start time (HH:MM) |
| duration | integer | Duration in minutes (default: 60) |
| customer_name | string | Customer display name |
| customer_email | string | Customer email for confirmation |
| customer_phone | string | Customer phone number |
| notes | string | Booking notes |
curl -X POST https://salesbooth.com/api/v1/bookings \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"staff_id": "staff_xxxxx",
"date": "2026-03-20",
"start_time": "10:00",
"customer_name": "Alice Johnson",
"customer_email": "alice@example.com"
}'{
"error": false,
"data": {
"booking_id": "book_xxxxx",
"tenant_id": "tenant_xxxxx",
"deal_id": null,
"line_item_id": null,
"product_id": "prod_xxxxx",
"product_name": "60-min Consultation",
"staff_id": "staff_xxxxx",
"staff_name": "Jane Smith",
"customer_id": null,
"customer_name": null,
"customer_email": null,
"customer_phone": null,
"booking_date": "2026-03-20",
"start_time": "10:00",
"end_time": "11:00",
"status": "held",
"hold_expires_at": "2026-03-20T10:10:00Z",
"notes": null,
"created_at": "2026-03-20T09:55:00Z",
"updated_at": "2026-03-20T09:55:00Z"
}
}action field with action-specific parameters.| Field | Type | Description |
|---|---|---|
| action required | string | confirm, cancel, complete, no_show, or reschedule |
action: confirm
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to associate with this booking confirmation |
| line_item_id | string | Specific line item within the deal |
| customer_id | string | Customer to associate with this booking |
action: cancel
| Field | Type | Description |
|---|---|---|
| reason | string | Cancellation reason |
action: complete / no_show
No additional fields required.
action: reschedule
| Field | Type | Description |
|---|---|---|
| product_id required | string | Bookable product for the new slot |
| staff_id required | string | Staff member for the new slot |
| date required | string | New date in YYYY-MM-DD format |
| start_time required | string | New start time in HH:MM format |
| duration | integer | Duration in minutes (5–480, default: 60) |
| customer_name | string | Customer display name |
| customer_email | string | Customer email address |
| customer_phone | string | Customer phone number |
| notes | string | Booking notes |
curl -X PATCH "https://salesbooth.com/api/v1/bookings?id=book_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "action": "confirm", "deal_id": "deal_xxxxx" }'curl -X PATCH "https://salesbooth.com/api/v1/bookings?id=book_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"action": "reschedule",
"product_id": "prod_xxxxx",
"staff_id": "staff_xxxxx",
"date": "2026-04-10",
"start_time": "14:00"
}'curl -X DELETE "https://salesbooth.com/api/v1/bookings?id=book_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Availability
Check available booking slots for a bookable product. Used by the widget during checkout to present a date picker with real-time slot availability. Authenticated with a publishable key (sb_pub_*) or a secret key with products:read scope.
| Parameter | Type | Description |
|---|---|---|
| product_id required | string | Bookable product to check |
| date | string | Date in YYYY-MM-DD format — returns available time slots for that day |
| month | string | Month in YYYY-MM format — returns all dates with availability in the month |
| staff_id | string | Optional — filter slots for a specific staff member |
curl "https://salesbooth.com/api/v1/availability?product_id=prod_xxxxx&date=2026-03-20" \
-H "Authorization: Bearer sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"available_slots": [
{ "time": "09:00", "staff_id": "staff_aaa", "staff_name": "Alice" },
{ "time": "10:30", "staff_id": "staff_aaa", "staff_name": "Alice" },
{ "time": "14:00", "staff_id": "staff_bbb", "staff_name": "Bob" }
]
}
}curl "https://salesbooth.com/api/v1/availability?product_id=prod_xxxxx&month=2026-03" \
-H "Authorization: Bearer sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"available_dates": ["2026-03-18", "2026-03-19", "2026-03-20", "2026-03-25"]
}
}Negotiations
Agent-to-agent and human-to-agent deal negotiation protocol. Propose terms, counter-propose, accept, or reject — all tracked with full history and optional AI suggestions. Requires agent:negotiate scope for write operations.
Who needs this: Use Negotiations if you want buyers, agents, or both parties to propose and counter-propose deal terms (discount, payment terms, delivery dates) before a deal is finalised. It is also the foundation for AI-automated bargaining between software agents.
When to skip this: For fixed-price deals where no back-and-forth is needed, you can ignore this section entirely. Create the deal, add line items, and transition directly to in_progress.
Prerequisite: An API key with agent:negotiate scope.
Negotiation Lifecycle
┌─────────────────────────────────────────────────────────────────────┐
│ NEGOTIATION STATE MACHINE │
│ │
│ ┌──────────┐ │
│ propose │ │ counter │
│ ┌───────────▶│ proposed │─────────────┐ │
│ │ │ │ │ │
│ │ └──────────┘ ▼ │
│ (no prior │ │ │ ┌──────────────┐ │
│ history) │ accept reject │ counter_ │ │
│ │ │ │ │ proposed │ │
│ │ ▼ ▼ └──────────────┘ │
│ │ ┌──────────────┐ │ │ │
│ │ │ accepted / │ accept reject │
│ │ │ rejected │ │ │ │
│ │ └──────────────┘ ▼ ▼ │
│ │ ┌──────────────┐ │
│ │ │ accepted / │ │
│ │ │ rejected │ │
│ │ └──────────────┘ │
│ │ │
│ Note: rounds can continue indefinitely until accepted, │
│ rejected, or the proposal expires (expires_at reached) │
└─────────────────────────────────────────────────────────────────────┘| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal identifier |
curl "https://salesbooth.com/api/v1/deal-negotiations?deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"deal_id": "deal_abc123",
"status": "counter_proposed",
"total_rounds": 2,
"rounds": [
{
"round": 1,
"type": "proposal",
"proposed_by": "agent",
"proposed_terms": { "discount_percent": 15, "quantity": 10 },
"message": "Volume order — requesting 15% discount",
"expires_at": "2026-03-16T10:00:00Z",
"created_at": "2026-03-09T10:00:00Z"
},
{
"round": 2,
"type": "counter",
"proposed_by": "merchant",
"proposed_terms": { "discount_percent": 10, "quantity": 10 },
"message": "We can offer 10% for this volume",
"expires_at": "2026-03-18T10:00:00Z",
"created_at": "2026-03-09T10:05:00Z"
}
]
}
}{
"error": false,
"data": {
"deal_id": "deal_abc123",
"suggested_counter": { "discount_percent": 8 },
"acceptable_range": { "discount_min": 5, "discount_max": 12 },
"confidence": "high",
"comparable_deals": 34,
"avg_accepted_discount": 7.8
}
}propose, counter, accept, or reject.| Field | Type | Description |
|---|---|---|
| action required | string | propose, counter, accept, reject |
| deal_id required | string | The deal identifier |
| proposed_terms | object | Terms to propose (required for propose and counter). Free-form key/value pairs: e.g. discount_percent, quantity, payment_terms |
| message | string | Optional message to accompany the proposal |
| expires_at | string | ISO 8601 expiry for this round (defaults to 7 days) |
| reason | string | Rejection reason (optional, used with reject) |
curl -X POST "https://salesbooth.com/api/v1/deal-negotiations?action=propose&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"proposed_terms": { "discount_percent": 15, "payment_terms": "net_30" },
"message": "Volume order — requesting 15% discount with net-30 terms",
"expires_at": "2026-03-19T00:00:00Z"
}'curl -X POST "https://salesbooth.com/api/v1/deal-negotiations?action=counter&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"proposed_terms": { "discount_percent": 10, "payment_terms": "net_15" },
"message": "We can offer 10% with net-15 terms"
}'curl -X POST "https://salesbooth.com/api/v1/deal-negotiations?action=accept&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'curl -X POST "https://salesbooth.com/api/v1/deal-negotiations?action=reject&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "reason": "Terms do not meet minimum margin requirements" }'negotiation.suggestion_generated webhook.| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal identifier |
| current_terms | object | The latest proposed terms (if omitted, uses the most recent round from history) |
curl -X POST "https://salesbooth.com/api/v1/deal-negotiations?action=suggest&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"current_terms": { "discount_percent": 15, "payment_terms": "net_30" }
}'{
"error": false,
"data": {
"suggestions": [
{
"strategy": "balanced",
"proposed_terms": { "discount_percent": 10, "payment_terms": "net_15" },
"confidence": 0.82,
"rationale": "10% discount with net-15 closes 69% of similar deals; net-30 adds payment risk"
},
{
"strategy": "aggressive",
"proposed_terms": { "discount_percent": 7, "payment_terms": "net_7" },
"confidence": 0.61,
"rationale": "Maximum margin protection; 41% acceptance rate at this price point"
}
],
"data_quality": {
"comparable_deals": 52,
"confidence_level": "high"
}
}
}Deal Templates
Reusable deal templates for programmatic offer generation. Create templates with pre-defined line items and terms, then instantiate them into real deals.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific template |
| status | string | Filter: active, inactive |
| category | string | Filter by category |
| search | string | Search by name or description |
| limit | integer | Max results, 1–100 (default: 50) |
| offset | integer | Pagination offset |
curl https://salesbooth.com/api/v1/deal-templates \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| name required | string | Template name |
| description | string | Template description |
| category | string | Template category |
| default_terms | object | Default deal terms (currency, tax_rate, etc.) |
| line_items | array | Pre-defined line items with product_id, quantity, unit_price |
| metadata | object | Custom metadata |
curl -X POST https://salesbooth.com/api/v1/deal-templates \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise Starter Pack",
"category": "enterprise",
"default_terms": { "currency": "USD", "tax_rate": 0.10 },
"line_items": [
{ "product_id": "prod_xxxxx", "quantity": 1, "unit_price": 999.00 }
]
}'| Field | Type | Description |
|---|---|---|
| customer_id | string | Customer for the new deal |
| overrides | object | Override default terms (currency, tax_rate, etc.) |
curl -X POST "https://salesbooth.com/api/v1/deal-templates?id=dtpl_xxxxx&action=create-deal" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "customer_id": "cust_xxxxx" }'curl -X PATCH "https://salesbooth.com/api/v1/deal-templates?id=dtpl_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Template Name" }'curl -X DELETE "https://salesbooth.com/api/v1/deal-templates?id=dtpl_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Delegations
Grant AI agents scoped, time-limited access with spending caps and delegation chains. Use delegations to give sub-agents constrained permissions without sharing your primary API key.
Who needs this: Use Delegations if you are building an AI agent that needs to spawn sub-agents or hand off tasks to other services while strictly limiting what those sub-agents are allowed to spend or do. Common use cases include procurement bots, approval-gate workflows, and multi-tenant SaaS where resellers act on behalf of customers.
When to skip this: If all API calls originate from your own server using a single API key, you don’t need delegations. They are only relevant when you are distributing authority across multiple keys, agents, or third parties.
Prerequisite: An API key with agent:execute scope and trust level ≥ 2 (Established).
Chain depth & spending caps: Delegations form a chain — Agent A can delegate a subset of its permissions to Agent B, which can sub-delegate to Agent C. The platform enforces:
- Downward only: A child delegation can never grant more permissions than the parent delegation it was created from.
- Spending caps cascade: Each transaction is checked against the per-transaction, daily, and monthly limits of every delegation in the chain. The most restrictive limit always wins.
- Expiry propagates: If any delegation in the chain expires or is revoked, all sub-delegations become invalid immediately.
Include delegation: Pass the X-Delegation-ID header on API requests or MCP calls to operate within a delegation’s spending limits and scope constraints.
action=verify to check validity and remaining budget.| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific delegation |
| action | string | verify — check validity and budget; summary — dashboard summary; pending — incoming proposals (agent key only) |
| limit | integer | Max results (default: 50) |
| offset | integer | Pagination offset |
curl "https://salesbooth.com/api/v1/delegations?id=del_xxxxx&action=verify" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"delegation_id": "del_xxxxx",
"status": "active",
"valid": true,
"allowed_actions": ["deals:read", "deals:write", "agent:negotiate"],
"spending": {
"max_transaction_amount": 5000.00,
"max_daily_amount": 25000.00,
"max_monthly_amount": null,
"spent_today": 3750.00,
"remaining_today": 21250.00,
"spent_this_month": 48200.00
},
"expires_at": "2026-06-01T00:00:00Z",
"grantee_key_id": "key_agent_xxxxx"
}
}| Field | Type | Description |
|---|---|---|
| grantee_agent_key_id required | string | The agent API key ID to delegate to |
| allowed_actions required | array | Permitted actions (scope-based): deals:read, deals:write, agent:negotiate, deals:sign, customers:read, customers:write |
| max_transaction_amount | number | Per-transaction spending cap (null = no limit) |
| max_daily_amount | number | Daily spending cap (resets at midnight UTC) |
| max_monthly_amount | number | Monthly spending cap (resets on the 1st) |
| expires_at | string | Delegation expiry (ISO 8601, must be in the future) |
| description | string | Human-readable description of the delegation purpose (max 500 chars) |
| approval_threshold | number | Transaction amount above which approval is required |
| approval_steps | object | Multi-step approval workflow configuration |
| approval_timeout | integer | Hours before pending approval auto-expires (0–720) |
| auto_approve_policy | object | Conditions under which transactions are auto-approved |
| alert_threshold_pct | integer | Spending alert when daily/monthly usage reaches this percentage (1–100) |
| signing_authority_acknowledged | boolean | Required when allowed_actions includes sign authority. Confirms principal authorises agent to execute binding agreements. |
curl -X POST https://salesbooth.com/api/v1/delegations \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"grantee_agent_key_id": "key_agent_xxxxx",
"allowed_actions": ["deals:read", "deals:write", "agent:negotiate"],
"max_transaction_amount": 5000.00,
"max_daily_amount": 25000.00,
"expires_at": "2026-06-01T00:00:00Z"
}'{
"error": false,
"data": {
"delegation_id": "del_xxxxx",
"tenant_id": "tenant_abc123",
"grantor_type": "user",
"grantor_id": "42",
"grantee_agent_key_id": "key_agent_xxxxx",
"description": null,
"max_transaction_amount": "5000.00",
"max_daily_amount": "25000.00",
"max_monthly_amount": null,
"spent_today": "0.00",
"spent_this_month": "0.00",
"reserved_today": "0.00",
"reserved_this_month": "0.00",
"available_today": "25000.00",
"available_this_month": null,
"allowed_actions": ["deals:read", "deals:write", "agent:negotiate"],
"delegation_chain": [],
"expires_at": "2026-06-01 00:00:00",
"revoked_at": null,
"created_at": "2026-03-12 10:00:00",
"updated_at": "2026-03-12 10:00:00",
"approval_threshold": null,
"approval_steps": null,
"approval_timeout": null,
"auto_approve_policy": null,
"alert_threshold_pct": null
}
}curl -X PUT "https://salesbooth.com/api/v1/delegations?id=del_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "max_transaction_amount": 2500.00, "expires_at": "2026-05-01T00:00:00Z" }'curl -X DELETE "https://salesbooth.com/api/v1/delegations?id=del_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Agent Proposal Flow
Agents can propose delegations to each other without requiring direct API access. The target agent receives the proposal and accepts, rejects, or counters it. The negotiation cycle repeats until both parties agree or the proposal expires.
delegations:read scope.| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max results (default: 50, max: 100) |
| offset | integer | Pagination offset (default: 0) |
curl "https://salesbooth.com/api/v1/delegations/proposals" \
-H "Authorization: Bearer sb_agent_key_do_not_use"{
"error": false,
"data": {
"proposals": [
{
"id": "dprop_xxxxx",
"status": "pending",
"proposer_api_key_id": "key_agent_aaaaa",
"target_api_key_id": "key_agent_bbbbb",
"proposed_actions": ["discover", "negotiate"],
"proposed_spending_limits": { "max_transaction_amount": 1000.00 },
"proposed_duration_hours": 168,
"message": "Requesting delegation to negotiate deals on your behalf",
"round": 1,
"expires_at": "2026-03-23T10:00:00Z",
"created_at": "2026-03-16T10:00:00Z"
}
],
"pagination": { "total": 1, "limit": 50, "offset": 0 }
}
}delegate action in the proposer’s own delegation scope.| Field | Type | Description |
|---|---|---|
| target_api_key_id required | string | API key ID of the target agent |
| proposed_actions required | array | Actions being proposed (e.g. discover, negotiate) |
| max_transaction_amount | number | Proposed per-transaction spending cap |
| max_daily_amount | number | Proposed daily spending cap |
| max_monthly_amount | number | Proposed monthly spending cap |
| proposed_duration_hours | integer | Delegation duration from acceptance (default: 168 = 1 week) |
| message | string | Optional message to the target agent explaining the request |
curl -X POST https://salesbooth.com/api/v1/delegations/proposals \
-H "Authorization: Bearer sb_agent_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"target_api_key_id": "key_agent_bbbbb",
"proposed_actions": ["discover", "negotiate"],
"max_transaction_amount": 1000.00,
"proposed_duration_hours": 168,
"message": "Requesting delegation to negotiate deals on your behalf"
}'{
"error": false,
"data": {
"id": "dprop_xxxxx",
"status": "pending",
"proposer_api_key_id": "key_agent_aaaaa",
"target_api_key_id": "key_agent_bbbbb",
"proposed_actions": ["discover", "negotiate"],
"proposed_spending_limits": { "max_transaction_amount": 1000.00 },
"proposed_duration_hours": 168,
"message": "Requesting delegation to negotiate deals on your behalf",
"round": 1,
"expires_at": "2026-03-23T10:00:00Z",
"created_at": "2026-03-16T10:00:00Z"
}
}curl -X POST "https://salesbooth.com/api/v1/delegations/proposals/dprop_xxxxx/accept" \
-H "Authorization: Bearer sb_agent_key_do_not_use"{
"error": false,
"data": {
"proposal": { "id": "dprop_xxxxx", "status": "accepted", "resolved_at": "2026-03-16T10:05:00Z" },
"delegation": { "id": "del_yyyyy", "status": "active", "allowed_actions": ["discover", "negotiate"] }
}
}| Field | Type | Description |
|---|---|---|
| reason | string | Optional rejection reason sent back to the proposer |
curl -X POST "https://salesbooth.com/api/v1/delegations/proposals/dprop_xxxxx/reject" \
-H "Authorization: Bearer sb_agent_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "reason": "Spending limit too high for current context" }'| Field | Type | Description |
|---|---|---|
| proposed_actions required | array | Counter-proposed actions (must be subset of original) |
| max_transaction_amount | number | Counter-proposed per-transaction cap |
| max_daily_amount | number | Counter-proposed daily cap |
| max_monthly_amount | number | Counter-proposed monthly cap |
| proposed_duration_hours | integer | Counter-proposed duration in hours |
| message | string | Optional message explaining the counter-proposal |
curl -X POST "https://salesbooth.com/api/v1/delegations/proposals/dprop_xxxxx/counter" \
-H "Authorization: Bearer sb_agent_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"proposed_actions": ["discover"],
"max_transaction_amount": 500.00,
"proposed_duration_hours": 72,
"message": "Accepting discover only with lower spending limit"
}'{
"error": false,
"data": {
"id": "dprop_zzzzz",
"status": "pending",
"previous_proposal_id": "dprop_xxxxx",
"proposed_actions": ["discover"],
"proposed_spending_limits": { "max_transaction_amount": 500.00 },
"proposed_duration_hours": 72,
"round": 2,
"expires_at": "2026-03-23T10:05:00Z",
"created_at": "2026-03-16T10:05:00Z"
}
}Using a Delegation
Pass the X-Delegation-ID header to operate within a delegation’s constraints. All spending is tracked against the delegation’s caps and propagated up the chain.
curl -X POST https://salesbooth.com/api/v1/deals \
-H "Authorization: Bearer sb_agent_key_do_not_use" \
-H "X-Delegation-ID: del_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "customer_id": "cust_xxxxx", "currency": "USD" }'
# If the deal value would exceed max_transaction_amount, returns 429 spending_limit_exceededProduct Families
Top-level groupings for organizing related products. Families define the structure for the product configurator.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific family |
| status | string | Filter: active, archived |
| search | string | Search by name |
curl https://salesbooth.com/api/v1/product-families \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| name required | string | Family name |
| description | string | Family description |
| slug | string | URL-friendly slug |
| sort_order | integer | Display order |
| image_url | string | Family image URL |
| status | string | active or archived |
curl -X POST https://salesbooth.com/api/v1/product-families \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "name": "Cloud Infrastructure", "description": "Cloud hosting and compute products" }'curl -X PATCH "https://salesbooth.com/api/v1/product-families?id=pf_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Family Name" }'curl -X DELETE "https://salesbooth.com/api/v1/product-families?id=pf_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Option Groups
Reusable configuration building blocks. Option groups define selectable options (e.g. colours, sizes, add-ons) that can be linked to products.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific group |
| status | string | Filter: active, archived |
| search | string | Search by name |
| include_options | boolean | Include options in response |
curl https://salesbooth.com/api/v1/option-groups?include_options=true \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| name required | string | Group name (e.g. “Colour”, “Storage”) |
| description | string | Group description |
| selection_type | string | single or multiple |
| is_required | boolean | Whether selection is required |
| min_selections | integer | Minimum options the customer must select (0–100) |
| max_selections | integer | Maximum options the customer can select (0–100) |
| display_style | string | cards, dropdown, pills, swatches, checkboxes |
| sort_order | integer | Display order |
curl -X POST https://salesbooth.com/api/v1/option-groups \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Storage Capacity",
"selection_type": "single",
"is_required": true,
"display_style": "cards"
}'Options within Groups
curl -X POST "https://salesbooth.com/api/v1/option-groups?id=og_xxxxx&action=options" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "name": "256 GB", "price_modifier": 100.00, "sort_order": 1 }'curl -X PATCH "https://salesbooth.com/api/v1/option-groups?id=og_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Group Name" }'curl -X DELETE "https://salesbooth.com/api/v1/option-groups?id=og_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Bundle Rules
Define discount conditions when multiple options are selected together. Bundle rules automatically apply pricing incentives.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific rule |
| product_id | string | Filter by product |
| is_active | boolean | Filter by active status |
curl https://salesbooth.com/api/v1/bundle-rules?product_id=prod_xxxxx \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| name required | string | Rule name |
| product_id required | string | Product this rule applies to |
| option_ids required | array | Option IDs that trigger the bundle |
| discount_type required | string | percentage or fixed |
| discount_value required | number | Discount amount (percent or fixed) |
| min_options | integer | Minimum options needed (default: all) |
curl -X POST https://salesbooth.com/api/v1/bundle-rules \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Full Suite Bundle",
"product_id": "prod_xxxxx",
"option_ids": ["opt_aaa", "opt_bbb", "opt_ccc"],
"discount_type": "percentage",
"discount_value": 15,
"min_options": 3
}'curl -X PATCH "https://salesbooth.com/api/v1/bundle-rules?id=br_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "discount_value": 20 }'curl -X DELETE "https://salesbooth.com/api/v1/bundle-rules?id=br_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Compatibility Rules
Define requires, excludes, and includes_price relationships between options. Compatibility rules prevent invalid configurations.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific rule |
| product_id | string | Filter by product |
| rule_type | string | Filter: requires, excludes, includes_price |
| source_option_id | string | Filter by source option |
curl https://salesbooth.com/api/v1/compatibility-rules?product_id=prod_xxxxx \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| source_option_id required | string | The triggering option |
| target_option_id required | string | The affected option |
| rule_type required | string | requires, excludes, or includes_price |
| product_id required | string | Product this rule applies to |
| message | string | User-facing message when rule triggers |
curl -X POST https://salesbooth.com/api/v1/compatibility-rules \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"source_option_id": "opt_ssd",
"target_option_id": "opt_raid",
"rule_type": "requires",
"product_id": "prod_xxxxx",
"message": "RAID controller requires SSD storage"
}'curl -X PATCH "https://salesbooth.com/api/v1/compatibility-rules?id=cr_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "message": "Updated compatibility message" }'curl -X DELETE "https://salesbooth.com/api/v1/compatibility-rules?id=cr_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Product Option Groups
Link option groups to specific products and manage per-product option availability overrides. Controls which option groups appear for a product and which individual options are available or hidden.
| Parameter | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
| action | string | availability to list per-option availability overrides |
curl "https://salesbooth.com/api/v1/product-option-groups?product_id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
| group_id | string | Option group to link (required when not setting availability) |
| sort_order_override | integer | Override display order for this product |
| is_required_override | boolean | Override whether the group is required |
| action | string | availability to set per-option availability (requires option_id and is_available) |
curl -X POST https://salesbooth.com/api/v1/product-option-groups \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"group_id": "grp_xxxxx",
"sort_order_override": 1,
"is_required_override": true
}'curl -X PATCH https://salesbooth.com/api/v1/product-option-groups \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"group_id": "grp_xxxxx",
"sort_order_override": 2
}'| Field | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
| group_id | string | Option group to unlink |
curl -X DELETE https://salesbooth.com/api/v1/product-option-groups \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"group_id": "grp_xxxxx"
}'Product Rules
Retrieve an aggregate view of all configuration rules (compatibility and bundle rules) for a product, and validate option selections against those rules.
| Parameter | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
curl "https://salesbooth.com/api/v1/product-rules?product_id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
| selected_options required | array | Array of selected option IDs to validate |
curl -X POST https://salesbooth.com/api/v1/product-rules \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"selected_options": ["opt_ssd_512", "opt_ram_32gb"]
}'{
"error": false,
"data": {
"valid": true,
"violations": [],
"price_adjustments": [
{ "reason": "Bundle discount: SSD + 32GB RAM", "amount": -50.00 }
]
}
}Configuration (CPQ)
Unified CPQ (Configure, Price, Quote) API for AI agents and widget interactions. Get product schemas with rules, validate configurations, and calculate pricing.
| Parameter | Type | Description |
|---|---|---|
| product_id required | string | Product identifier |
curl https://salesbooth.com/api/v1/configuration?product_id=prod_xxxxx \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/configuration?action=families \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| option_ids required | array | Array of selected option IDs |
curl -X POST "https://salesbooth.com/api/v1/configuration?product_id=prod_xxxxx&action=validate" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "option_ids": ["opt_aaa", "opt_bbb"] }'{
"error": false,
"data": {
"valid": true,
"violations": [],
"bundles_applied": ["Full Suite Bundle"]
}
}| Field | Type | Description |
|---|---|---|
| option_ids required | array | Array of selected option IDs |
curl -X POST "https://salesbooth.com/api/v1/configuration?product_id=prod_xxxxx&action=price" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "option_ids": ["opt_aaa", "opt_bbb"] }'{
"error": false,
"data": {
"base_price": 999.00,
"option_total": 200.00,
"bundle_discount": -59.85,
"subtotal": 1139.15,
"breakdown": [
{ "option_id": "opt_aaa", "name": "256 GB SSD", "price": 100.00 },
{ "option_id": "opt_bbb", "name": "RAID Controller", "price": 100.00 }
]
}
}Webhooks
Subscribe to real-time events. Salesbooth will send an HTTP POST to your endpoint when events occur.
| Field | Type | Description |
|---|---|---|
| url required | string | HTTPS endpoint URL to receive webhook events |
| events required | array | Event types to subscribe to (e.g. deal.created) |
| description | string | Optional human-readable description (max 255 chars) |
curl -X POST https://salesbooth.com/api/v1/webhooks \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"url": "https://myapp.com/webhooks/salesbooth",
"events": ["deal.created"]
}'active by default. To pause a webhook, use PATCH with "status": "paused".| Field | Description |
|---|---|
| url | New endpoint URL |
| events | Array of event type strings to subscribe to |
| status | active or paused |
| description | Human-readable description |
curl -X PATCH "https://salesbooth.com/api/v1/webhooks?id=wh_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"status": "paused",
"events": ["deal.created", "deal.closed", "payment.received"]
}'curl -X DELETE "https://salesbooth.com/api/v1/webhooks?id=wh_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Description |
|---|---|
| id required | Webhook ID to retrieve deliveries for |
| status | Filter by delivery status: pending, success, or failed |
| event_type | Filter by event type (e.g. deal.created) |
| limit | Number of results to return (default 50, max 100) |
| offset | Pagination offset (default 0) |
curl "https://salesbooth.com/api/v1/webhooks/deliveries?id=wh_xxxxx&status=failed&limit=25" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"deliveries": [
{
"delivery_id": "dlv_abc123",
"webhook_id": "wh_xxxxx",
"event_type": "deal.created",
"status": "failed",
"attempts": 3,
"response_code": 500,
"error_message": "Connection timeout",
"delivered_at": null,
"created_at": "2026-03-09T10:30:00Z"
}
],
"pagination": {
"total": 42,
"limit": 25,
"offset": 0,
"count": 1
}
}
}since_sequence or since_timestamp to resume from a known position.| Parameter | Description |
|---|---|
| since_sequence | Return events after this sequence number (cursor-based pagination) |
| since_timestamp | Return events after this ISO 8601 timestamp |
| event_type | Filter by event type (e.g. deal.created) |
| status | Filter by delivery status: pending, delivered, partially_delivered, or failed |
| limit | Number of results to return (1–100, default 50) |
curl "https://salesbooth.com/api/v1/webhooks/events?since_sequence=1500&limit=50" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"events": [
{
"id": "evt_abc123",
"sequence": 1501,
"event_type": "deal.created",
"status": "delivered",
"created_at": "2026-03-09T10:30:00Z"
}
],
"cursor": {
"last_sequence": 1550,
"has_more": true
}
}
}/webhooks/replay or /webhooks/retry to re-deliver them.| Parameter | Description |
|---|---|
| event_type | Filter by event type |
| webhook_id | Filter by webhook ID |
| limit | Number of results to return (default 50) |
| offset | Pagination offset (default 0) |
curl "https://salesbooth.com/api/v1/webhooks/dead_letter?webhook_id=wh_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"dead_letters": [
{
"delivery_id": "dlv_abc123",
"webhook_id": "wh_xxxxx",
"event_id": "evt_abc123",
"event_type": "deal.created",
"attempts": 3,
"last_error": "Connection refused",
"last_attempted_at": "2026-03-09T12:00:00Z",
"created_at": "2026-03-09T10:30:00Z"
}
],
"pagination": {
"total": 7,
"limit": 50,
"offset": 0,
"count": 1
}
}
}| Field | Type | Description |
|---|---|---|
| id required | query | Webhook ID to send the test event to |
| event_type | body | Event type to simulate (default: deal.created) |
curl -X POST "https://salesbooth.com/api/v1/webhooks/test?id=wh_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"event_type": "deal.created"
}'{
"error": false,
"success": true,
"data": {
"success": true,
"response_code": 200,
"response_body": "OK",
"latency_ms": 45,
"error_message": null,
"event_type": "deal.created"
}
}X-Salesbooth-Replayed: true header.| Field | Description |
|---|---|
| webhook_id required | Target webhook to replay events to |
| since_sequence | Replay events after this sequence number |
| since_timestamp | Replay events after this ISO 8601 timestamp |
| event_type | Only replay a specific event type |
curl -X POST https://salesbooth.com/api/v1/webhooks/replay \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"webhook_id": "wh_xxxxx",
"since_timestamp": "2026-03-09T00:00:00Z",
"event_type": "deal.created"
}'{
"error": false,
"success": true,
"data": {
"replayed": 42,
"webhook_id": "wh_xxxxx",
"has_more": false,
"events": [
{
"event_id": "evt_abc123",
"event_sequence": 1501,
"event_type": "deal.created",
"delivery_id": "dlv_new456"
}
]
}
}pending and attempts immediate re-delivery.| Field | Description |
|---|---|
| delivery_id required | ID of the failed delivery to retry |
curl -X POST https://salesbooth.com/api/v1/webhooks/retry \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"delivery_id": "dlv_abc123"
}'{
"error": false,
"success": true,
"data": {
"delivery": {
"delivery_id": "dlv_abc123",
"status": "success",
"attempts": 4,
"response_code": 200,
"error_message": null,
"retried_at": "2026-03-09T18:00:00Z"
}
}
}| Field | Description |
|---|---|
| retention_days | Days of events to retain (default 30) |
| batch_size | Maximum records to delete per run (default 10000) |
curl -X POST https://salesbooth.com/api/v1/webhooks/cleanup \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"retention_days": 30,
"batch_size": 10000
}'{
"error": false,
"success": true,
"data": {
"cutoff_date": "2026-02-09T00:00:00Z",
"retention_days": 30,
"events_deleted": 4821,
"deliveries_deleted": 9203
}
}Available Events
| Deal Events | |
| deal.created | A new deal was created |
| deal.updated | A deal's fields were updated |
| deal.status_changed | A deal transitioned to a new status |
| deal.closed | A deal was closed |
| deal.fulfilled | A deal was fulfilled |
| deal.expired | A deal expired without being completed |
| deal.unsigned | A deal's signature was removed |
| deal.signature_added | A signature was added to a deal |
| deal.signature_timeout | A deal's signature request timed out |
| deal.item_added | A line item was added to a deal |
| deal.item_removed | A line item was removed from a deal |
| deal.item_updated | A line item on a deal was updated |
| deal.discount_applied | A discount was applied to a deal |
| deal.partially_accepted | A deal was partially accepted by the customer |
| deal.created_configured | A deal was created from a saved product configuration |
| deal.payment_received | A payment was received for a deal |
| deal.payment_failed | A payment attempt failed |
| deal.payment_refunded | A payment was refunded (full or partial) |
| deal.payment_overdue | A payment is overdue |
| deal.settlement_created | A settlement was created for a deal |
| deal.settlement_initiated | A settlement was initiated |
| deal.settlement_completed | A settlement completed successfully |
| deal.settlement_failed | A settlement failed |
| deal.settlement_all_completed | All settlements for a deal completed |
| deal.participant_invited | A participant was invited to a deal |
| deal.participant_accepted | A participant accepted their deal invitation |
| deal.participant_withdrawn | A participant withdrew from a deal |
| deal.participant_completed | A participant completed their deal obligations |
Negotiation Events — Legacy (deal.*) Maintained for backward compatibility. Both families fire simultaneously. | |
| deal.negotiation_proposed | A negotiation proposal was made (minimal payload) |
| deal.negotiation_counter_proposed | A counter-proposal was made (minimal payload) |
| deal.negotiation_accepted | A negotiation proposal was accepted (minimal payload) |
| deal.negotiation_rejected | A negotiation proposal was rejected (minimal payload) |
Negotiation Events — Current (negotiation.*) Richer payloads. Prefer these for new integrations. | |
| negotiation.proposed | Initial proposal submitted — includes proposer, terms, and expires_at |
| negotiation.countered | Counter-proposal made — includes round_number, previous_terms, and new_terms for diffing |
| negotiation.accepted | Proposal accepted — includes final_terms and total_rounds |
| negotiation.rejected | Proposal rejected — includes reason and round_number |
| negotiation.expired | A negotiation round expired without resolution |
| negotiation.suggestion_generated | AI generated a negotiation suggestion |
| Contract Events | |
| contract.signed | A contract was signed |
| contract.activated | A contract was activated |
| contract.terminated | A contract was terminated |
| contract.tamper_detected | A tamper attempt was detected on a contract |
| contract.auto_renewed | A contract was automatically renewed |
| contract.renewal_failed | A contract auto-renewal attempt failed |
| contract.renewal_opted_out | A customer opted out of contract auto-renewal |
| contract.renewal_upcoming | A contract renewal is approaching |
| Subscription Events | |
| subscription.created | A subscription was created |
| subscription.renewed | A subscription was renewed |
| subscription.renewal_upcoming | A subscription renewal is approaching |
| subscription.paused | A subscription was paused |
| subscription.resumed | A paused subscription was resumed |
| subscription.cancelled | A subscription was cancelled |
| subscription.upgraded | A subscription was upgraded to a higher tier |
| subscription.downgraded | A subscription was downgraded to a lower tier |
| subscription.changed | A subscription was modified |
| subscription.past_due | A subscription payment is past due |
| subscription.suspended | A subscription was suspended due to non-payment |
| subscription.cycle_changed | A subscription billing cycle was changed |
| subscription.payment_failed | A subscription payment attempt failed |
| subscription.payment_retried | A failed subscription payment was retried |
| subscription.proration_credit | A proration credit was applied to a subscription |
| subscription.meter_added | A usage meter was added to a subscription |
| subscription.meter_removed | A usage meter was removed from a subscription |
| subscription.usage_recorded | Usage was recorded against a subscription meter |
| subscription.usage_charges_applied | Usage charges were applied to a subscription |
| Customer Events | |
| customer.created | A new customer was created |
| customer.updated | A customer record was updated |
| customer.deleted | A customer was deleted |
| Booking Events | |
| booking.created | A new booking was created |
| booking.cancelled | A booking was cancelled |
| booking.completed | A booking was completed |
| booking.no_show | A customer did not show up for a booking |
| booking.held | A booking slot was placed on hold |
| booking.hold_expired | A booking hold expired without confirmation |
| booking.rescheduled | A booking was rescheduled to a new time |
| Billing Events | |
| billing.credit_added | Credits were added to the account balance |
| billing.credit_deducted | Credits were deducted from the account balance |
| billing.auto_topup | An automatic top-up was triggered |
| billing.low_balance | Account credit balance dropped below the threshold |
| billing.widget_degraded | The widget entered degraded mode due to insufficient credits |
| Delegation Events | |
| delegation.proposed | A delegation proposal was submitted |
| delegation.accepted | A delegation proposal was accepted |
| delegation.rejected | A delegation proposal was rejected |
| delegation.countered | A counter-proposal was made to a delegation |
| delegation.granted | A delegation was granted to an agent |
| delegation.revoked | A delegation was revoked |
| delegation.expired | A delegation expired |
| delegation.budget_warning | A delegation is approaching its budget limit |
| Agent Events | |
| agent.approval_required | An agent action requires human approval |
| agent.approval_resent | An approval request was resent |
| agent.approval_escalated | An approval request was escalated |
| agent.anomaly_detected | Anomalous agent behaviour was detected |
| agent.spending_ceiling | An agent reached its spending ceiling |
| agent.trust_cap_exceeded | An agent exceeded its trust cap |
| agent.trust.level_changed | An agent's trust level changed |
| agent.trust.abuse_detected | Abuse was detected from a trusted agent |
| agent.trust.decay_warning | An agent's trust score is decaying |
| agent.trust.credential_issued | A trust credential was issued to an agent |
| agent.trust.credential_presented | An agent presented a trust credential |
| agent.trust.credential_revoked | An agent's trust credential was revoked |
| Agent Workflow Events | |
| agent.workflow.planned | An agent workflow plan was created |
| agent.workflow.approved | An agent workflow was approved to proceed |
| agent.workflow.completed | An agent workflow completed successfully |
| agent.workflow.failed | An agent workflow failed |
| agent.workflow.cancelled | An agent workflow was cancelled |
| agent.workflow.degraded | An agent workflow entered degraded mode |
| agent.workflow.step_completed | A step in an agent workflow completed |
| agent.workflow.step_retried | A workflow step was retried |
| agent.workflow.dead_lettered | A workflow step was moved to the dead-letter queue |
| Escrow Events | |
| escrow.created | An escrow was created |
| escrow.released | An escrow was released |
| escrow.refunded | An escrow was refunded |
| escrow.condition_fulfilled | An escrow condition was fulfilled |
| escrow.reauthorization_pending | An escrow requires reauthorization |
| escrow.reauthorized | An escrow was successfully reauthorized |
| escrow.reauthorization_failed | An escrow reauthorization attempt failed |
| Federation Events | |
| federation.settlement_completed | A cross-tenant federated settlement completed |
| federation.settlement_failed | A cross-tenant federated settlement failed |
| federation.escrow_disputed | A federated escrow was disputed |
| federation.escrow_expired | A federated escrow expired |
| Product & Catalogue Events | |
| product_family.created | A product family was created |
| product_family.updated | A product family was updated |
| product_family.deleted | A product family was deleted |
| option_group.created | An option group was created |
| option_group.updated | An option group was updated |
| option_group.deleted | An option group was deleted |
| option.created | A product option was created |
| option.updated | A product option was updated |
| option.deleted | A product option was deleted |
| bundle_rule.created | A bundle rule was created |
| bundle_rule.updated | A bundle rule was updated |
| bundle_rule.deleted | A bundle rule was deleted |
| compatibility_rule.created | A compatibility rule was created |
| compatibility_rule.updated | A compatibility rule was updated |
| compatibility_rule.deleted | A compatibility rule was deleted |
| Saved Configuration Events | |
| saved_config.created | A product configuration was saved |
| saved_config.viewed | A saved configuration was viewed |
| saved_config.converted | A saved configuration was converted to a deal |
| saved_config.expiring | A saved configuration is about to expire |
| saved_config.expired | A saved configuration expired |
| Portal Events | |
| portal.payment_completed | A payment was completed via the customer portal |
| portal.contract_signed | A contract was signed via the customer portal |
| portal.change_requested | A change was requested via the customer portal |
| Consent Events | |
| consent.expiring_soon | A customer consent record is about to expire |
| consent.expired | A customer consent record expired |
| Tenant & Trust Events | |
| tenant.trust_level_changed | A tenant's trust level changed |
| tenant.trust_promoted | A tenant was promoted to a higher trust tier |
| tenant.trust_level_decay_warning | A tenant's trust level is decaying |
| trust.demoted | A trust entity was demoted to a lower tier |
| trust.fraud_signal | A fraud signal was raised against a trust entity |
| Workflow Events | |
| workflow.step_approved | A workflow step was approved |
| workflow.sell.proposal_received | A sell workflow received a proposal |
| workflow.fulfill.delivered | A fulfillment workflow delivered its outcome |
| System & Infrastructure Events | |
| job.completed | A background job completed |
| job.failed | A background job failed |
| queue.depth_alert | A job queue depth exceeded its alert threshold |
| system.job_dead_letter | A job was moved to the dead-letter queue |
| service.circuit_opened | A circuit breaker opened due to repeated failures |
| service.circuit_closed | A circuit breaker closed after recovery |
| circuit-breaker.backoff-increased | A circuit breaker increased its backoff interval |
| encryption.rekey_completed | An encryption re-key operation completed |
| security.csp_spike | A spike in CSP violation reports was detected |
| Other Events | |
| payment.received | A payment was received |
| credential.issued | A verifiable credential was issued |
| api_key.rotated | An API key was rotated |
Webhook Payload Format
All webhook payloads follow the same structure. Each delivery includes an X-Salesbooth-Signature header for verification.
{
"id": "evt_abc123",
"event": "deal.created",
"created_at": "2026-03-09T10:30:00Z",
"data": {
"id": "deal_abc123",
"customer_id": "cust_xxxxx",
"status": "draft",
"currency": "USD",
"subtotal": "0.00",
"total": "0.00",
"created_at": "2026-03-09T10:30:00Z"
}
}{
"id": "evt_def456",
"event": "deal.status_changed",
"created_at": "2026-03-09T11:00:00Z",
"data": {
"id": "deal_abc123",
"previous_status": "draft",
"new_status": "in_progress",
"changed_at": "2026-03-09T11:00:00Z"
}
}{
"id": "evt_ghi789",
"event": "deal.payment_received",
"created_at": "2026-03-09T12:00:00Z",
"data": {
"deal_id": "deal_abc123",
"amount": "1099.00",
"currency": "USD",
"payment_intent_id": "pi_xxxxx",
"status": "succeeded"
}
}{
"id": "evt_jkl012",
"event": "customer.created",
"created_at": "2026-03-09T10:00:00Z",
"data": {
"id": "cust_xxxxx",
"company": "Acme Corp",
"status": "active",
"created_at": "2026-03-09T10:00:00Z"
}
}{
"id": "evt_mno345",
"event": "contract.signed",
"created_at": "2026-03-09T13:00:00Z",
"data": {
"id": "contract_xxxxx",
"deal_id": "deal_abc123",
"customer_id": "cust_xxxxx",
"signed_by": "customer",
"signed_at": "2026-03-09T13:00:00Z",
"signature_method": "ed25519"
}
}{
"id": "evt_pqr678",
"event": "negotiation.proposed",
"created_at": "2026-03-09T14:00:00Z",
"data": {
"deal_id": "deal_abc123",
"round_number": 1,
"proposer": "agent",
"proposed_terms": { "discount_percent": 15, "payment_terms": "net_30" },
"message": "Volume order — requesting 15% discount",
"expires_at": "2026-03-16T14:00:00Z"
}
}{
"id": "evt_stu901",
"event": "negotiation.countered",
"created_at": "2026-03-09T15:00:00Z",
"data": {
"deal_id": "deal_abc123",
"round_number": 2,
"proposer": "merchant",
"previous_terms": { "discount_percent": 15, "payment_terms": "net_30" },
"new_terms": { "discount_percent": 10, "payment_terms": "net_15" },
"message": "We can offer 10% with net-15 terms",
"expires_at": "2026-03-18T15:00:00Z"
}
}{
"id": "evt_vwx234",
"event": "negotiation.accepted",
"created_at": "2026-03-09T16:00:00Z",
"data": {
"deal_id": "deal_abc123",
"total_rounds": 2,
"final_terms": { "discount_percent": 10, "payment_terms": "net_15" },
"accepted_by": "agent",
"accepted_at": "2026-03-09T16:00:00Z"
}
}{
"id": "evt_yza567",
"event": "subscription.created",
"created_at": "2026-03-09T17:00:00Z",
"data": {
"deal_id": "deal_abc123",
"billing_cycle": "monthly",
"status": "active",
"next_renewal_at": "2026-04-09T17:00:00Z",
"amount": "99.00",
"currency": "USD"
}
}{
"id": "evt_bcd890",
"event": "subscription.past_due",
"created_at": "2026-04-09T17:05:00Z",
"data": {
"deal_id": "deal_abc123",
"amount": "99.00",
"currency": "USD",
"failure_reason": "card_declined",
"grace_period_ends_at": "2026-04-16T17:05:00Z",
"retry_count": 1
}
}{
"id": "evt_efg123",
"event": "escrow.created",
"created_at": "2026-03-10T10:00:00Z",
"data": {
"id": "escrow_xxxxx",
"deal_id": "deal_abc123",
"amount": "2500.00",
"currency": "USD",
"release_condition": "delivery_confirmed",
"expires_at": "2026-06-10T10:00:00Z"
}
}{
"id": "evt_hij456",
"event": "subscription.renewed",
"created_at": "2026-04-09T17:00:00Z",
"data": {
"deal_id": "deal_abc123",
"renewal_deal_id": "deal_def456",
"billing_cycle": "monthly",
"base_amount": "99.00",
"metered_amount": "2.55",
"total_amount": "101.55",
"currency": "USD",
"next_renewal_at": "2026-05-09T17:00:00Z"
}
}{
"id": "evt_klm789",
"event": "subscription.cancelled",
"created_at": "2026-03-12T10:00:00Z",
"data": {
"deal_id": "deal_abc123",
"cancelled_at": "2026-03-12T10:00:00Z",
"effective_at": "2026-04-09T17:00:00Z",
"end_of_period": true,
"reason": "Customer requested cancellation"
}
}Signature Verification
Every webhook delivery includes two headers: X-Salesbooth-Signature (v1= + HMAC-SHA256 of the timestamp + raw body) and X-Salesbooth-Timestamp (Unix timestamp of delivery). Always verify both to prevent replay attacks. Reject events where the timestamp is older than 300 seconds (5 minutes).
const crypto = require('crypto');
const TOLERANCE_SECONDS = 300; // 5 minutes
function verifyWebhook(rawBody, signature, timestamp, secret) {
// 1. Reject stale events
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (age > TOLERANCE_SECONDS) throw new Error('Webhook timestamp too old');
// 2. Compute expected signature: v1= + HMAC-SHA256(timestamp.body, secret)
const payload = `${timestamp}.${rawBody}`;
const expected = 'v1=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
// 3. Constant-time comparison (signature starts with "v1=" — not valid hex)
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) throw new Error('Invalid signature');
return true;
}
// Express.js example
app.post('/webhooks/salesbooth', express.raw({ type: 'application/json' }), (req, res) => {
try {
verifyWebhook(
req.body.toString(),
req.headers['x-salesbooth-signature'],
req.headers['x-salesbooth-timestamp'],
process.env.WEBHOOK_SECRET
);
const event = JSON.parse(req.body);
console.log('Verified event:', event.event, event.id);
res.sendStatus(200);
} catch (err) {
console.error('Webhook verification failed:', err.message);
res.sendStatus(400);
}
});Audit Trail
Every entity has an immutable, cryptographically chained audit trail. Entries cannot be modified or deleted.
| Parameter | Description |
|---|---|
| entity_type required | deal, contract, customer, product |
| entity_id required | The entity identifier |
| limit | Max results, 1–100 (default: 50) |
| offset | Pagination offset |
audit:export scope. Supports three export modes: single entity (provide entity_type + entity_id), date range (provide start_date + end_date), or bulk by type (provide entity_type alone). Exports include a verification_hash (SHA-256) covering the entire payload for tamper detection. Use format=csv to receive a spreadsheet-compatible download.| Parameter | Type | Description |
|---|---|---|
| entity_type | string | deal, contract, customer, or product |
| entity_id | string | Specific entity ID (required with entity_type for single-entity export) |
| start_date | string | Start of date range (Y-m-d or Y-m-d H:i:s) |
| end_date | string | End of date range (Y-m-d or Y-m-d H:i:s) |
| format | string | json (default) or csv |
| limit | integer | Max entries to return, 1–10000 (default: 1000) |
| offset | integer | Pagination offset |
curl "https://salesbooth.com/api/v1/audit/export?entity_type=deal&entity_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/audit/export?start_date=2026-01-01&end_date=2026-03-31&format=csv" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-o audit-q1-2026.csv{
"error": false,
"data": {
"export_version": "1.0",
"exported_at": "2026-03-18T10:00:00Z",
"tenant_id": "tenant_xxxxx",
"entity_type": "deal",
"entity_id": "deal_abc123",
"audit_entries": [
{
"id": "audit_001",
"action": "deal.created",
"actor": "user_xxxxx",
"created_at": "2026-03-10T09:00:00Z",
"entry_hash": "b4e2a1..."
}
],
"verification_hash": "sha256:c9f3b2..."
}
}Intelligence
AI-powered deal scoring, pricing analytics, risk assessment, pipeline forecasting, and model calibration. All endpoints require the deals:read scope; config write operations additionally require intelligence:write.
Scoring model: Each deal score (0–100) is a weighted composite of configurable factors. Default weights: deal_size (25%), customer_history (30%), pipeline_stage (20%), engagement (15%), time_in_stage (10%). Weights must sum to 1.0 and can be overridden per tenant via PATCH ?type=config.
| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal to score |
curl "https://salesbooth.com/api/v1/intelligence?type=score&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"deal_id": "deal_abc123",
"score": 74,
"confidence": "high",
"label": "Good",
"factors": {
"deal_size": { "score": 80, "weight": 0.25, "contribution": 20 },
"customer_history": { "score": 70, "weight": 0.30, "contribution": 21 },
"pipeline_stage": { "score": 60, "weight": 0.20, "contribution": 12 },
"engagement": { "score": 85, "weight": 0.15, "contribution": 12.75 },
"time_in_stage": { "score": 67, "weight": 0.10, "contribution": 6.7 }
},
"recommendations": [
{ "type": "discount", "amount": 5, "rationale": "Volume threshold reached — 5% discount increases close probability by ~12%" }
],
"risk_flags": [],
"scored_at": "2026-03-12T10:00:00Z"
}
}{
"error": false,
"data": {
"deal_id": "deal_abc123",
"suggested_price": 4750.00,
"current_price": 5000.00,
"confidence": "medium",
"rationale": "Similar deals with this customer segment close 23% more often at 4,500–5,000 range",
"comparable_deals": 47,
"win_rate_at_suggested": 0.71,
"win_rate_at_current": 0.58
}
}curl "https://salesbooth.com/api/v1/intelligence?type=risk&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"deal_id": "deal_abc123",
"overall_risk": "medium",
"risk_score": 42,
"factors": [
{
"factor": "time_in_stage",
"severity": "high",
"description": "Deal has been in 'in_progress' for 18 days (median: 7 days)",
"mitigation": "Follow up with customer — consider offering a limited-time incentive"
},
{
"factor": "deal_size",
"severity": "low",
"description": "Deal value is within normal range for this customer segment",
"mitigation": null
}
],
"churn_probability": 0.31,
"assessed_at": "2026-03-12T10:00:00Z"
}
}{
"error": false,
"data": {
"deal_id": "deal_abc123",
"predicted_close_date": "2026-04-08",
"confidence_interval": {
"p25": "2026-03-28",
"p50": "2026-04-08",
"p75": "2026-04-22"
},
"close_probability_30d": 0.68,
"close_probability_60d": 0.84,
"methodology": "Gradient boosted on 2,400 historical deals in this pipeline stage"
}
}| Parameter | Type | Description |
|---|---|---|
| period | string | 30d, 60d, 90d, or all (default: all) |
{
"error": false,
"data": {
"period": "90d",
"forecast_revenue": 284500.00,
"weighted_pipeline": 142750.00,
"currency": "USD",
"by_stage": {
"in_progress": { "count": 23, "total_value": 187000.00, "weighted": 93500.00 },
"pending_signature": { "count": 8, "total_value": 97500.00, "weighted": 87750.00 }
},
"generated_at": "2026-03-12T10:00:00Z"
}
}{
"error": false,
"data": {
"buckets": [
{ "score_range": "0-20", "avg_score": 12, "win_rate": 0.08, "sample_size": 34 },
{ "score_range": "21-40", "avg_score": 31, "win_rate": 0.22, "sample_size": 67 },
{ "score_range": "41-60", "avg_score": 51, "win_rate": 0.45, "sample_size": 112 },
{ "score_range": "61-80", "avg_score": 71, "win_rate": 0.69, "sample_size": 89 },
{ "score_range": "81-100","avg_score": 88, "win_rate": 0.86, "sample_size": 54 }
],
"calibration_error": 0.04,
"total_deals": 356
}
}| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal to fetch history for |
| limit | integer | Max snapshots (default: 20) |
{
"error": false,
"data": {
"deal_id": "deal_abc123",
"history": [
{ "score": 62, "scored_at": "2026-03-01T09:00:00Z", "trigger": "deal_created" },
{ "score": 68, "scored_at": "2026-03-05T14:30:00Z", "trigger": "item_added" },
{ "score": 74, "scored_at": "2026-03-09T10:00:00Z", "trigger": "negotiation_accepted" }
]
}
}{
"error": false,
"data": {
"accuracy": 0.79,
"brier_score": 0.14,
"deals_analyzed": 356,
"current_weights": {
"deal_size": 0.25, "customer_history": 0.30,
"pipeline_stage": 0.20, "engagement": 0.15, "time_in_stage": 0.10
},
"suggested_weights": {
"deal_size": 0.20, "customer_history": 0.35,
"pipeline_stage": 0.25, "engagement": 0.10, "time_in_stage": 0.10
},
"estimated_accuracy_improvement": 0.04
}
}curl "https://salesbooth.com/api/v1/intelligence?type=pricing" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"intelligence:write scope. Weights must sum to 1.0.| Field | Type | Description |
|---|---|---|
| weights | object | Keys: deal_size, customer_history, pipeline_stage, engagement, time_in_stage. Values must sum to 1.0. |
| thresholds | object | Score thresholds for label assignment: good (default: 70), fair (default: 40), poor (below 40) |
curl -X PATCH "https://salesbooth.com/api/v1/intelligence?type=config" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"weights": {
"deal_size": 0.20,
"customer_history": 0.35,
"pipeline_stage": 0.25,
"engagement": 0.10,
"time_in_stage": 0.10
},
"thresholds": { "good": 72, "fair": 45 }
}'| Field | Type | Description |
|---|---|---|
| weights required | object | Proposed weight configuration to test (must sum to 1.0) |
curl -X POST "https://salesbooth.com/api/v1/intelligence?type=backtest" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"weights": {
"deal_size": 0.20, "customer_history": 0.35,
"pipeline_stage": 0.25, "engagement": 0.10, "time_in_stage": 0.10
}
}'{
"error": false,
"data": {
"proposed_accuracy": 0.83,
"current_accuracy": 0.79,
"improvement": 0.04,
"deals_tested": 356,
"top_gains": [
{ "stage": "pending_signature", "accuracy_delta": 0.09 }
]
}
}| Parameter | Type | Description |
|---|---|---|
| stage required | string | Pipeline stage to query. One of: draft, in_progress, pending_payment, pending_signature, awaiting_signatures, closed, cancelled, expired |
curl "https://salesbooth.com/api/v1/intelligence?type=pipeline_deals&stage=in_progress" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"stage": "in_progress",
"count": 3,
"deals": [
{
"deal_id": "deal_abc123",
"title": "Enterprise Plan — Acme Corp",
"total": 12500.00,
"status": "in_progress",
"created_at": "2026-03-01T09:00:00Z",
"customer_name": "Acme Corp",
"score": 74,
"win_probability": 0.68,
"days_since_update": 3
}
]
}
}curl "https://salesbooth.com/api/v1/intelligence?type=pricing_suggestions_bulk" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"count": 2,
"suggestions": [
{
"deal_id": "deal_abc123",
"title": "Enterprise Plan — Acme Corp",
"total": 12500.00,
"currency": "USD",
"product_name": "Enterprise Plan",
"current_discount": 0,
"suggested_discount": 8.5,
"avg_winning_discount": 8.5,
"win_probability": 0.42,
"probability_lift": 0.034,
"score": 51,
"reason": "Products like \"Enterprise Plan\" close at 8.5% avg discount on similar deals"
}
]
}
}| Parameter | Type | Description |
|---|---|---|
| days | integer | Lookback window in days, 7–365 (default: 90) |
curl "https://salesbooth.com/api/v1/intelligence?type=suggestion_analytics&days=90" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"period_days": 90,
"total_suggestions": 148,
"accepted": 62,
"rejected": 41,
"pending": 45,
"acceptance_rate": 0.60,
"by_type": {
"discount": { "total": 89, "accepted": 41, "acceptance_rate": 0.62 },
"follow_up": { "total": 35, "accepted": 14, "acceptance_rate": 0.54 },
"upsell": { "total": 24, "accepted": 7, "acceptance_rate": 0.44 }
},
"outcome_impact": {
"accepted_deal_win_rate": 0.71,
"rejected_deal_win_rate": 0.48
}
}
}| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal to generate counter-terms for |
| current_terms | string | JSON-encoded object of the buyer's proposed terms to counter (optional). If omitted, suggestions are based on the deal's current state. |
curl "https://salesbooth.com/api/v1/intelligence?type=counter_terms&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -G "https://salesbooth.com/api/v1/intelligence" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
--data-urlencode "type=counter_terms" \
--data-urlencode "deal_id=deal_abc123" \
--data-urlencode 'current_terms={"discount_pct":15,"payment_terms":"net60"}'{
"error": false,
"data": {
"deal_id": "deal_abc123",
"counter_terms": [
{
"field": "discount_pct",
"buyer_proposed": 15,
"counter_offer": 8,
"rationale": "8% is within the range that closes 73% of deals in this segment; 15% reduces margin below threshold"
},
{
"field": "payment_terms",
"buyer_proposed": "net60",
"counter_offer": "net30",
"rationale": "net30 is standard for this customer tier; offer net45 as a concession if needed"
}
],
"win_probability_if_accepted": 0.71,
"generated_at": "2026-03-12T10:00:00Z"
}
}SDK Example — Score a Deal
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
// Score a deal and get recommendations
const result = await sb.intelligence.score('deal_abc123');
console.log('Score:', result.data.score); // 74
console.log('Confidence:', result.data.confidence); // "high"
result.data.recommendations.forEach(rec => {
console.log(`Recommendation: ${rec.type} ${rec.amount}% — ${rec.rationale}`);
});
// Get pricing suggestion
const suggestion = await sb.intelligence.pricingSuggestion('deal_abc123');
console.log('Suggested price:', suggestion.data.suggested_price);
// Get risk assessment
const risk = await sb.intelligence.risk('deal_abc123');
if (risk.data.overall_risk === 'high') {
console.warn('High-risk deal:', risk.data.factors.map(f => f.factor));
}Search
Global search across all entities. Searches customers (via encrypted blind indexes), products, deals, and contracts.
| Parameter | Description |
|---|---|
| q required | Search query (min 2 characters) |
| limit | Max results (default: 10, max: 25) |
Activity Feed
Real-time event log and notification management. The activity feed records all significant events across deals, customers, payments, and more. Configure notification rules to route alerts to email, webhook, or in-app channels.
| Parameter | Type | Description |
|---|---|---|
| event_type | string | Filter by event type (e.g. deal.created, payment.received) |
| actor_type | string | Filter by actor: user, agent, or system |
| entity_type | string | Filter by entity: deal, contract, customer |
| limit | integer | Max results (default: 50, max: 100) |
| offset | integer | Pagination offset |
curl "https://salesbooth.com/api/v1/activity?event_type=deal.created&limit=20" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/activity?action=notifications" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/activity?action=rules" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| event_type required | string | Event pattern to match (e.g. deal.signature_added, payment.*) |
| channel required | string | Delivery channel: email, webhook, or in_app |
| name | string | Rule display name |
curl -X POST "https://salesbooth.com/api/v1/activity?action=create_rule" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"event_type": "deal.signature_added",
"channel": "email",
"name": "Notify on deal sign"
}'action=mark_all_read to mark all as read at once.| Field | Type | Description |
|---|---|---|
| notification_ids required | array | Array of notification IDs to mark as read |
curl -X POST "https://salesbooth.com/api/v1/activity?action=mark_read" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "notification_ids": ["notif_abc", "notif_def"] }'curl -X PATCH "https://salesbooth.com/api/v1/activity?action=update_rule&rule_id=rule_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "channel": "webhook" }'curl -X DELETE "https://salesbooth.com/api/v1/activity?action=delete_rule&rule_id=rule_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/activity?action=preferences" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"deal.created": { "email": true, "in_app": true, "webhook": false },
"deal.signature_added": { "email": true, "in_app": true, "webhook": true },
"payment.received": { "email": true, "in_app": false, "webhook": true }
}
}curl -X PUT "https://salesbooth.com/api/v1/activity?action=preferences" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal.created": { "email": true, "in_app": true, "webhook": false },
"deal.signature_added": { "email": true, "in_app": true, "webhook": true },
"payment.received": { "email": false, "in_app": true, "webhook": true }
}'Batch Operations
Execute up to 25 API operations in a single request. All operations run inside a transaction — if any write fails, all mutations are rolled back. Ideal for reducing round-trips in integrations and complex workflows.
Transactional. All write operations in a batch are atomic. Any failure rolls back all mutations. Read operations (GET) are always included in the response regardless of write failures.
method, resource, optional id, optional body, and optional version for optimistic locking.| Field | Type | Description |
|---|---|---|
| operations required | array | Array of operation objects (max 25). Supported resources: deals, customers, products, contracts |
curl -X POST https://salesbooth.com/api/v1/batch \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "method": "POST", "resource": "customers", "body": { "name": "Acme Corp" } },
{ "method": "POST", "resource": "deals", "body": { "title": "Enterprise Plan", "amount": 5000 } },
{ "method": "PATCH", "resource": "deals", "id": "deal_existing", "body": { "status": "active" }, "version": 3 }
]
}'{
"error": false,
"data": {
"results": [
{ "index": 0, "status": 201, "data": { "id": "cust_xxxxx" } },
{ "index": 1, "status": 201, "data": { "id": "deal_xxxxx" } },
{ "index": 2, "status": 200, "data": { "id": "deal_existing" } }
],
"committed": true
}
}AI Agent Integration
Salesbooth provides first-class support for AI agents through tool schemas, MCP protocol, and an agent SDK. Agents can discover, negotiate, and execute deals automatically.
Tool Definitions
The /api/v1/tools endpoint returns curated tool definitions optimized for LLM function-calling. Each tool maps to a high-level business operation.
| Parameter | Description |
|---|---|
| format | universal (default), openai, or anthropic |
| category | Filter: discovery, deals, products, negotiation, execution |
curl https://salesbooth.com/api/v1/tools?format=openai \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Available Tools
MCP Server
The /api/v1/mcp endpoint implements the Model Context Protocol over HTTP using JSON-RPC 2.0.
initialize, tools/list, tools/call, resources/list, resources/read, and ping.curl -X POST https://salesbooth.com/api/v1/mcp \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{}}'curl -X POST https://salesbooth.com/api/v1/mcp \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"2","method":"tools/list","params":{}}'Agent SDK
The Agent SDK provides getToolDefinitions() and executeTool() for seamless LLM integration.
const SalesboothAgent = require('./salesbooth-agent');
const agent = SalesboothAgent.init({
apiKey: 'sb_test_example_key_do_not_use',
delegationId: 'del_xxxxx' // optional
});
// Get tool definitions for OpenAI
const tools = agent.getToolDefinitions('openai');
// Execute a tool
const deals = await agent.executeTool('discover_deals', {
category: 'software',
max_price: 10000
});const Salesbooth = require('salesbooth');
// Creates an agent client (requires salesbooth-agent.js)
const agent = Salesbooth.agent({
apiKey: 'sb_test_example_key_do_not_use',
delegationId: 'del_xxxxx'
});
const tools = agent.getToolDefinitions('anthropic');Agent Scopes
Delegation Tokens
Use delegation tokens to grant agents scoped access with spending limits. Pass the delegation ID via the X-Delegation-ID header.
curl https://salesbooth.com/api/v1/tools \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "X-Delegation-ID: del_xxxxx"Agent Discovery
Merchant control plane for AI-visible offers. Toggle product discoverability, manage pricing rules for agents, and view agent analytics.
curl https://salesbooth.com/api/v1/agent-discovery-admin?action=dashboard \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/agent-discovery-admin?action=products \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| product_id required | string | Product to toggle |
| enabled required | boolean | Whether agents can discover this product |
curl -X POST https://salesbooth.com/api/v1/agent-discovery-admin?action=toggle \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "product_id": "prod_xxxxx", "enabled": true }'| Field | Type | Description |
|---|---|---|
| product_ids required | array | Array of product IDs |
| enabled required | boolean | Discoverability status |
curl -X POST https://salesbooth.com/api/v1/agent-discovery-admin?action=bulk-toggle \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "product_ids": ["prod_aaa", "prod_bbb"], "enabled": true }'Agent Workflows
Autonomous deal orchestration. Agents can plan and execute multi-step deal workflows within delegation spending limits.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific workflow |
| status | string | Filter: planned, executing, completed, cancelled |
| delegation_id | string | Filter by delegation |
| limit | integer | Max results (default: 50) |
curl https://salesbooth.com/api/v1/agent-workflow?status=executing \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| delegation_id required | string | Delegation providing spending limits |
| intent required | string | Natural language description of the workflow goal |
| constraints | object | Additional constraints (max_price, category, etc.) |
curl -X POST https://salesbooth.com/api/v1/agent-workflow \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"delegation_id": "del_xxxxx",
"intent": "Find and negotiate the best enterprise software deal under $5000",
"constraints": { "max_price": 5000, "category": "software" }
}'curl -X POST "https://salesbooth.com/api/v1/agent-workflow?id=wf_xxxxx&action=execute" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -X POST "https://salesbooth.com/api/v1/agent-workflow?id=wf_xxxxx&action=cancel" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Agent Trust
Per-API-key trust scoring, capability gating, and abuse protection. Agent trust levels determine transaction caps, allowed actions, and metered spending limits.
Who needs this: Agent Trust is relevant if you are building or integrating with autonomous AI agents that create or execute deals on behalf of users. Trust levels unlock higher transaction caps and advanced capabilities (delegation, federation) as an agent demonstrates a track record of reliable behaviour.
When to skip this: For standard server-side integrations using your own API key (not an agent key), trust levels do not apply. Human-driven integrations are not subject to trust caps.
Prerequisite: An agent API key (created with agent_key: true). Regular API keys are not subject to the trust tier system.
Trust Level Reference
Trust Cap Exception: Attempting a deal above your transaction cap returns 403 trust_cap_exceeded. The response includes details.transaction_cap (your current limit) and details.attempted_amount. Complete more deals successfully to earn a higher trust level.
| Parameter | Type | Description |
|---|---|---|
| action | string | history for trust level change log, locks for active capability locks |
| limit | integer | Max results for history (default: 50) |
curl https://salesbooth.com/api/v1/agent-trust \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"key_id": "key_abc123",
"trust_level": 2,
"trust_label": "Established",
"score": 62,
"transaction_cap": 5000,
"capabilities": ["deal_management", "negotiation", "templates", "intelligence"],
"locked_capabilities": [],
"next_level": { "level": 3, "label": "Trusted", "score_needed": 80 },
"statistics": {
"deals_completed": 7,
"deals_failed": 0,
"total_deal_value": 14350.00
}
}
}curl "https://salesbooth.com/api/v1/agent-trust?action=history&limit=10" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Verifiable Credentials
Agents can receive verifiable W3C credentials attesting to their trust level and completed deal history. Credentials can be presented to third-party tenants during federation to establish cross-tenant trust without starting from level 0.
curl -X POST https://salesbooth.com/api/v1/agent-trust-credentials \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "credential_type": "TrustLevelCredential" }'{
"error": false,
"data": {
"credential_id": "cred_xyz789",
"type": "TrustLevelCredential",
"trust_level": 2,
"issued_at": "2026-03-12T10:00:00Z",
"expires_at": "2026-09-12T10:00:00Z",
"jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
}
}curl https://salesbooth.com/api/v1/agent-trust-credentials \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Trust-Related Webhook Events
| agent.trust.level_changed | Trust level promoted or demoted |
| agent.trust_cap_exceeded | Agent attempted a deal above their trust cap |
| agent.trust.abuse_detected | Anomalous behaviour detected — capability lock applied |
| agent.trust.credential_issued | Verifiable credential issued to agent |
| agent.trust.credential_revoked | Previously issued credential revoked |
MCP Protocol
Salesbooth implements the Model Context Protocol (MCP) over HTTP using JSON-RPC 2.0. The MCP endpoint exposes 190 tools across 27 categories, resource browsing, prompt templates, and subscriptions — giving AI agents a structured, discoverable interface to the full Salesbooth platform.
Endpoint: POST https://salesbooth.com/api/v1/mcp
Protocol Version: 2024-11-05 — Server: salesbooth-mcp v2.0
Required scope: agent:discover (to connect). Individual tools require additional scopes — see the Scope Requirements table below.
1. Connection & Initialize Handshake
All MCP communication uses POST /api/v1/mcp with a JSON-RPC 2.0 body. Authenticate with your API key in the Authorization header. Begin every session with an initialize request to confirm capabilities.
initialize, tools/list, tools/call, resources/list, resources/templates/list, resources/read, resources/subscribe, resources/unsubscribe, prompts/list, prompts/get, and ping.| Header | Description |
|---|---|
| Authorization | Bearer <api_key> — required. Use an API key with at minimum agent:discover scope. |
| X-Delegation-ID | Optional delegation token ID to execute tools under a scoped delegation with spending limits. |
| Content-Type | application/json |
curl -X POST https://salesbooth.com/api/v1/mcp \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "initialize",
"params": {}
}'{
"jsonrpc": "2.0",
"id": "1",
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": { "listChanged": false },
"resources": { "subscribe": true, "listChanged": false },
"prompts": { "listChanged": false }
},
"serverInfo": {
"name": "salesbooth-mcp",
"version": "2.0"
}
}
}2. Tool Discovery — tools/list
Retrieve the full catalogue of 156 agent tools. Each tool includes a JSON Schema for its parameters, making it directly consumable by LLM function-calling APIs (OpenAI, Anthropic, etc.).
curl -X POST https://salesbooth.com/api/v1/mcp \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/list",
"params": {}
}'{
"jsonrpc": "2.0",
"id": "2",
"result": {
"tools": [
{
"name": "discover_deals",
"description": "Find available deals matching criteria. Returns deals that can be negotiated or accepted. Use this as the first step to find deals for a customer.",
"inputSchema": {
"type": "object",
"properties": {
"category": { "type": "string", "description": "Filter by product category" },
"min_price": { "type": "number" },
"max_price": { "type": "number" },
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
"pricing_model": {
"type": "string",
"enum": ["one_time", "recurring", "usage_based", "tiered"]
},
"limit": { "type": "integer", "minimum": 1, "maximum": 100 }
},
"required": [],
"additionalProperties": false
}
}
// ... 155 more tools
]
}
}Tool Categories
3. Tool Execution — tools/call
Call any tool by name with its parameters. The server validates your scope, executes the tool, and returns structured JSON content. If a tool fails, isError is true and the content describes the error.
{
"jsonrpc": "2.0",
"id": "<request_id>",
"method": "tools/call",
"params": {
"name": "<tool_name>",
"arguments": { /* tool parameters */ }
}
}{
"jsonrpc": "2.0",
"id": "<request_id>",
"result": {
"content": [
{ "type": "text", "text": "<JSON string of tool result>" }
],
"isError": false
}
}Workflow 1: Create a Deal from Products
Discover a customer → create a deal → add line items → transition to in_progress.
{
"jsonrpc": "2.0", "id": "w1a", "method": "tools/call",
"params": {
"name": "search_customers",
"arguments": { "query": "Acme Corp", "limit": 5 }
}
}{
"jsonrpc": "2.0", "id": "w1b", "method": "tools/call",
"params": {
"name": "create_deal",
"arguments": {
"customer_id": "cust_abc123",
"currency": "USD",
"deal_type": "one_time",
"description": "Enterprise software package"
}
}
}{
"jsonrpc": "2.0", "id": "w1c", "method": "tools/call",
"params": {
"name": "add_deal_item",
"arguments": {
"id": "deal_xyz789",
"product_id": "prod_platform_pro",
"quantity": 10,
"unit_price": 99.00
}
}
}{
"jsonrpc": "2.0", "id": "w1d", "method": "tools/call",
"params": {
"name": "transition_deal",
"arguments": { "id": "deal_xyz789", "status": "in_progress" }
}
}Workflow 2: Score a Deal and Get Recommendations
Use intelligence tools to score deal health and fetch pricing recommendations.
{
"jsonrpc": "2.0", "id": "w2a", "method": "tools/call",
"params": {
"name": "score_deal",
"arguments": { "deal_id": "deal_xyz789" }
}
}{
"jsonrpc": "2.0", "id": "w2b", "method": "tools/call",
"params": {
"name": "get_pricing_recommendations",
"arguments": { "deal_id": "deal_xyz789", "strategy": "balanced" }
}
}{
"jsonrpc": "2.0", "id": "w2b",
"result": {
"content": [{
"type": "text",
"text": "{\"score\":82,\"confidence\":\"high\",\"recommendations\":[{\"type\":\"discount\",\"amount\":5,\"rationale\":\"Volume threshold reached\"}]}"
}],
"isError": false
}
}Workflow 3: Negotiate Terms with a Counter-Proposal
Submit a proposal, retrieve the negotiation history, then counter. Requires agent:negotiate scope.
{
"jsonrpc": "2.0", "id": "w3a", "method": "tools/call",
"params": {
"name": "negotiate_terms",
"arguments": {
"deal_id": "deal_xyz789",
"action": "propose",
"terms": { "discount_percent": 10, "payment_terms": "net_30" }
}
}
}{
"jsonrpc": "2.0", "id": "w3b", "method": "tools/call",
"params": {
"name": "get_negotiation_history",
"arguments": { "deal_id": "deal_xyz789" }
}
}{
"jsonrpc": "2.0", "id": "w3c", "method": "tools/call",
"params": {
"name": "counter_proposal",
"arguments": {
"negotiation_id": "neg_abc456",
"terms": { "discount_percent": 7, "payment_terms": "net_15" },
"message": "We can offer 7% with net-15 terms."
}
}
}Workflow 4: Check Delegation Spending Limits
Before executing an expensive action, verify the active delegation has sufficient spending capacity.
{
"jsonrpc": "2.0", "id": "w4a", "method": "tools/call",
"params": {
"name": "get_delegation_scope",
"arguments": { "delegation_id": "del_xxxxx" }
}
}{
"jsonrpc": "2.0", "id": "w4a",
"result": {
"content": [{
"type": "text",
"text": "{\"delegation_id\":\"del_xxxxx\",\"scopes\":[\"deals:write\",\"agent:negotiate\"],\"spending_limit\":5000.00,\"spent\":1250.00,\"remaining\":3750.00,\"expires_at\":\"2026-12-31T23:59:59Z\"}"
}],
"isError": false
}
}Workflow 5: Search Across All Entities
Use the global search tool to find customers, products, deals, or contracts by keyword. Requires agent:discover scope.
{
"jsonrpc": "2.0", "id": "w5", "method": "tools/call",
"params": {
"name": "global_search",
"arguments": {
"query": "enterprise",
"types": ["deals", "customers", "products"],
"limit": 10
}
}
}{
"jsonrpc": "2.0", "id": "w5",
"result": {
"content": [{
"type": "text",
"text": "{\"results\":[{\"type\":\"deal\",\"id\":\"deal_xyz789\",\"label\":\"Enterprise Software Package\",\"score\":0.97},{\"type\":\"product\",\"id\":\"prod_platform_pro\",\"label\":\"Platform Pro (Enterprise)\",\"score\":0.91}],\"total\":12}"
}],
"isError": false
}
}4. Resource Browsing
Resources give agents read access to live tenant data as structured documents. Salesbooth provides static resources (schemas, documentation) and dynamic resources (live deal, product, and customer data).
List all resources — resources/list
{
"jsonrpc": "2.0", "id": "r1",
"method": "resources/list",
"params": {}
}{
"jsonrpc": "2.0", "id": "r1",
"result": {
"resources": [
{ "uri": "salesbooth://schemas/deal-terms", "name": "Deal Terms Schema", "mimeType": "application/schema+json" },
{ "uri": "salesbooth://schemas/state-machine", "name": "Deal State Machine", "mimeType": "application/json" },
{ "uri": "salesbooth://docs/tools", "name": "Agent Tool Reference", "mimeType": "text/plain" },
{ "uri": "salesbooth://deals", "name": "Active Deals", "mimeType": "application/json" },
{ "uri": "salesbooth://products", "name": "Product Catalog", "mimeType": "application/json" },
{ "uri": "salesbooth://customers", "name": "Customer Directory", "mimeType": "application/json" },
{ "uri": "salesbooth://widgets", "name": "Widget Configurations", "mimeType": "application/json" },
{ "uri": "salesbooth://intelligence/pipeline", "name": "Pipeline Analytics", "mimeType": "application/json" },
{ "uri": "salesbooth://delegations", "name": "Agent Delegations", "mimeType": "application/json" },
{ "uri": "salesbooth://contracts", "name": "Contracts", "mimeType": "application/json" },
{ "uri": "salesbooth://templates", "name": "Deal Templates", "mimeType": "application/json" },
{ "uri": "salesbooth://saved-configs", "name": "Saved Configurations", "mimeType": "application/json" },
{ "uri": "salesbooth://subscriptions", "name": "Subscriptions", "mimeType": "application/json" },
{ "uri": "salesbooth://webhooks", "name": "Webhook Endpoints", "mimeType": "application/json" }
]
}
}Read a resource — resources/read
{
"jsonrpc": "2.0", "id": "r2",
"method": "resources/read",
"params": { "uri": "salesbooth://deals" }
}{
"jsonrpc": "2.0", "id": "r2",
"result": {
"contents": [{
"uri": "salesbooth://deals",
"mimeType": "application/json",
"text": "{\"deals\":[{\"id\":\"deal_xyz789\",\"status\":\"in_progress\",\"total_amount\":\"990.00\",\"currency\":\"USD\",\"customer_id\":\"cust_abc123\"}],\"pagination\":{\"total\":42,\"limit\":20,\"offset\":0}}"
}]
}
}Dynamic resource templates — resources/templates/list
Resource templates allow reading a single entity by ID using parameterized URIs.
{
"jsonrpc": "2.0", "id": "r3",
"method": "resources/templates/list",
"params": {}
}{
"jsonrpc": "2.0", "id": "r3",
"result": {
"resourceTemplates": [
{ "uriTemplate": "salesbooth://deals/{deal_id}", "name": "Deal by ID", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://products/{product_id}", "name": "Product by ID", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://customers/{customer_id}", "name": "Customer by ID", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://contracts/{contract_id}", "name": "Contract by ID", "mimeType": "application/json" }
]
}
}{
"jsonrpc": "2.0", "id": "r4",
"method": "resources/read",
"params": { "uri": "salesbooth://deals/deal_xyz789" }
}5. Prompt Templates
Prompt templates are guided multi-step workflows. Agents can inject them as system/user messages to guide an LLM through complex operations without needing to know every individual tool call.
List prompts — prompts/list
{
"jsonrpc": "2.0", "id": "p1",
"method": "prompts/list",
"params": {}
}{
"jsonrpc": "2.0", "id": "p1",
"result": {
"prompts": [
{ "name": "create-deal", "description": "Guided deal creation workflow: select products, look up or create a customer, configure pricing, and submit the deal." },
{ "name": "negotiate-deal", "description": "Structured negotiation workflow: review current deal terms, apply intelligence scoring, propose or counter terms." },
{ "name": "configure-widget", "description": "Widget setup workflow: select a site, choose products, configure theme and steps, generate embed code." },
{ "name": "review-pipeline", "description": "Pipeline health check: analyze deal distribution by status, identify bottlenecks, get recommended actions." },
{ "name": "manage-delegation", "description": "Create or update agent delegations: define scope, spending limits, and expiry for sub-agents." }
]
}
}Get a prompt — prompts/get
{
"jsonrpc": "2.0", "id": "p2",
"method": "prompts/get",
"params": {
"name": "negotiate-deal",
"arguments": {
"deal_id": "deal_xyz789",
"strategy": "balanced"
}
}
}{
"jsonrpc": "2.0", "id": "p2",
"result": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "You are a deal negotiation agent for Salesbooth. Your task:\n1. Retrieve the current state of deal deal_xyz789 using check_deal_status.\n2. Score the deal using score_deal to understand its health and value.\n3. Review negotiation history via get_negotiation_history.\n4. Apply a balanced strategy: aim for fair value on both sides.\n5. Submit a counter-proposal or accept terms using negotiate_terms / counter_proposal.\nAlways verify scope before acting. Stop if spending limits are exceeded."
}
}
]
}
}6. Error Handling
MCP errors follow the JSON-RPC 2.0 error object format. Tool execution errors are returned as isError: true in the result (not as JSON-RPC errors) so the agent can continue the session.
{
"jsonrpc": "2.0",
"id": "3",
"error": {
"code": -32602,
"message": "Missing required parameter: name"
}
}{
"jsonrpc": "2.0",
"id": "4",
"result": {
"content": [{
"type": "text",
"text": "{\"error\":true,\"code\":\"insufficient_scope\",\"message\":\"Tool 'create_deal' requires scope 'deals:write'\"}"
}],
"isError": true
}
}7. Scope Requirements by Category
Each MCP tool requires one or more scopes. The base connection itself requires agent:discover. Grant additional scopes by creating an API key with the scopes your agent needs (see Scopes & Permissions).
8. Circuit Breaker & Retry Guidance
Tool execution is wrapped in a circuit breaker with a 10-second request timeout and a failure threshold of 5 consecutive failures within 60 seconds. If the circuit is open, the MCP server returns a service_unavailable error with retryable: true in the content.
{
"jsonrpc": "2.0", "id": "5",
"result": {
"content": [{
"type": "text",
"text": "{\"error\":true,\"code\":\"service_unavailable\",\"message\":\"Tool execution temporarily unavailable (circuit breaker open). Retry after a short delay.\",\"retryable\":true}"
}],
"isError": true
}
}When retryable is true, wait at least 30 seconds before retrying. Do not flood the endpoint — exponential backoff is recommended. A 503 HTTP response (rather than a JSON-RPC error) means the MCP server itself is unreachable; apply the same backoff strategy.
9. Cross-Instance Federation
The federation tool category implements the salesbooth-negotiate/1.0 protocol, allowing agents to discover offerings on remote Salesbooth instances and execute cross-instance deals without leaving the MCP session.
{
"jsonrpc": "2.0", "id": "f1", "method": "tools/call",
"params": {
"name": "discover_federation_partners",
"arguments": { "limit": 20 }
}
}{
"jsonrpc": "2.0", "id": "f2", "method": "tools/call",
"params": {
"name": "initiate_federation_negotiation",
"arguments": {
"remote_instance_url": "https://partner.salesbooth.com",
"remote_deal_id": "remote_deal_123",
"proposed_terms": { "discount_percent": 5 }
}
}
}Federation requests are authenticated end-to-end using tenant key pairs and signed JWT assertions. The remote instance validates the incoming salesbooth-negotiate/1.0 request against its federation discovery endpoint (/.well-known/salesbooth-federation) before accepting any proposal.
Cross-Instance Federation
The Federation API enables agents and tenants to discover offerings on remote Salesbooth instances and execute cross-instance deals using the salesbooth-negotiate/1.0 protocol. Federation is generally available — no preview flag is required.
Endpoint: POST /api/v1/federation/negotiate • GET /api/v1/federation/negotiate
Required scopes: agent:discover (GET), agent:negotiate (POST)
Authentication: Requests are signed end-to-end with tenant Ed25519 key pairs. Remote instances validate incoming envelopes against the originator’s DID document at /.well-known/salesbooth-federation.
Actions (POST)
GET Actions
curl https://salesbooth.com/api/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -X POST https://salesbooth.com/api/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"action": "discover",
"remote_url": "https://partner.salesbooth.com",
"query": { "category": "software", "currency": "USD" }
}'curl -X POST https://salesbooth.com/api/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"action": "send",
"remote_url": "https://partner.salesbooth.com",
"envelope": {
"deal_id": "deal_remote_123",
"proposed_terms": { "discount_percent": 10, "currency": "USD" },
"expires_at": "2026-03-13T12:00:00Z"
}
}'agent:discover scope.| Parameter | Type | Description |
|---|---|---|
| action | string | status (default) for capabilities manifest; audit to retrieve a transaction audit trail |
| xref_id | string | Cross-reference transaction ID — required when action=audit |
curl https://salesbooth.com/api/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/federation/negotiate?action=audit&xref_id=xref_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"agent:negotiate scope.| Field | Type | Description |
|---|---|---|
| action required | string | discover, send, negotiate, escrow, settle, or dispute |
| remote_url | string | Base URL of the remote Salesbooth instance (required for discover and send) |
| envelope | object | Signed negotiation envelope (required for send and negotiate) |
| query | object | Discovery query filters (used with action=discover): category, currency, etc. |
curl -X POST https://salesbooth.com/api/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"action": "discover",
"remote_url": "https://partner.salesbooth.com",
"query": { "category": "software", "currency": "USD" }
}'Instance Discovery
Remote instances advertise their federation support via a well-known endpoint. This is used during the handshake to verify the remote tenant’s public key and supported protocol versions.
GET /.well-known/salesbooth-federation| Parameter | Type | Description |
|---|---|---|
| tenant_id required | string | Tenant whose federation manifest to retrieve |
curl "https://salesbooth.com/api/v1/federation/discovery?tenant_id=tenant_xxxxx"{
"error": false,
"success": true,
"data": {
"manifest": {
"version": "1.0",
"tenant_id": "tenant_xxxxx",
"endpoints": {
"negotiate": "https://salesbooth.com/api/v1/federation/negotiate",
"did_document": "https://salesbooth.com/api/v1/did-document"
},
"capabilities": ["negotiate", "escrow", "settle", "dispute"],
"protocol_versions": ["salesbooth-negotiate/1.0"]
}
}
}| Parameter | Type | Description |
|---|---|---|
| tenant_id | string | Tenant whose public key to retrieve (required if key_id not provided). Pass _current to resolve from session. |
| key_id | string | Retrieve a specific signing key by ID (useful when verifying a signature that references a rotated key) |
| format | string | Key format: raw, pem, jwk, or all (default) |
| include_rotated | flag | Return all keys including rotated ones (omit for active key only) |
curl "https://salesbooth.com/api/v1/tenant/public-key?tenant_id=tenant_xxxxx"{
"error": false,
"data": {
"tenant_id": "tenant_xxxxx",
"key_id": "key_abc123",
"algorithm": "ed25519",
"key_version": 1,
"status": "active",
"created_at": "2026-01-15T08:00:00Z",
"public_key": "MCowBQYDK2VwAyEA...",
"public_key_pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA...\n-----END PUBLIC KEY-----",
"public_key_jwk": {
"kty": "OKP",
"crv": "Ed25519",
"kid": "key_abc123",
"x": "..."
}
}
}Trust requirement: Agents must be at Trust Level 3 (Trusted) to access federation capabilities. Cross-instance negotiation envelopes include a verifiable credential attesting to the originating agent’s trust level, which the remote instance validates before accepting any proposal.
DID Document
Every Salesbooth tenant publishes a W3C Decentralized Identifier (DID) document that remote instances use to verify deal signatures and federation credentials without trusting Salesbooth infrastructure.
| Parameter | Type | Description |
|---|---|---|
| tenant_id | string | Tenant whose DID document to retrieve. Omit to retrieve the document for the authenticated tenant. |
curl "https://salesbooth.com/api/v1/did-document?tenant_id=tenant_xxxxx"{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:web:salesbooth.com:tenant_xxxxx",
"verificationMethod": [
{
"id": "did:web:salesbooth.com:tenant_xxxxx#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:web:salesbooth.com:tenant_xxxxx",
"publicKeyMultibase": "z..."
}
],
"authentication": ["did:web:salesbooth.com:tenant_xxxxx#key-1"],
"service": [
{
"id": "did:web:salesbooth.com:tenant_xxxxx#federation",
"type": "SalesboothFederation",
"serviceEndpoint": "https://salesbooth.com/api/v1/federation/negotiate"
}
]
}Federation Peers
Manage the list of trusted remote Salesbooth instances your tenant federates with. Peers are used by the federation engine to pre-authorise cross-instance deal discovery and negotiation without requiring per-request trust verification. Requires agent:execute scope.
agent:discover scope.curl https://salesbooth.com/api/v1/federation-peers \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"peers": [
{
"id": 1,
"hostname": "partner.salesbooth.com",
"remote_tenant_id": "tenant_yyyyy",
"trust_level": 3,
"status": "active",
"created_at": "2026-01-15T10:00:00Z"
}
],
"count": 1,
"open_mode": false
}
}agent:execute scope.| Field | Type | Description |
|---|---|---|
| hostname required | string | Hostname of the remote Salesbooth instance (e.g. partner.salesbooth.com) |
| remote_tenant_id | string | Tenant ID on the remote instance to federate with specifically |
| trust_level | integer | Initial trust level for this peer (0–5, default: 1) |
| status | string | active or suspended (default: active) |
curl -X POST https://salesbooth.com/api/v1/federation-peers \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"hostname": "partner.salesbooth.com",
"remote_tenant_id": "tenant_yyyyy",
"trust_level": 3
}'{
"error": false,
"data": {
"id": 1,
"hostname": "partner.salesbooth.com",
"remote_tenant_id": "tenant_yyyyy",
"trust_level": 3,
"status": "active"
}
}agent:execute scope.| Field | Type | Description |
|---|---|---|
| status | string | active or suspended |
| trust_level | integer | New trust level (0–5) |
curl -X PATCH "https://salesbooth.com/api/v1/federation-peers?id=1" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "status": "suspended" }'{ "error": false, "data": { "updated": true, "id": 1 } }agent:execute scope.curl -X DELETE "https://salesbooth.com/api/v1/federation-peers?id=1" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{ "error": false, "data": { "deleted": true, "id": 1 } }API Keys
Create and manage API keys for your integration. Keys carry scopes that restrict which operations they can perform. This endpoint requires session (cookie) authentication — API keys cannot be used to manage other API keys.
Session authentication required. All /api/v1/keys endpoints require an active dashboard session. You cannot use an API key to create or revoke other API keys.
{
"error": false,
"data": [
{
"id": "key_xxxxx",
"name": "Production Integration",
"prefix": "sb_live_",
"scopes": ["deals:read", "customers:read"],
"created_at": "2026-01-15T10:00:00Z",
"last_used_at": "2026-03-10T08:30:00Z"
}
]
}| Field | Type | Description |
|---|---|---|
| name required | string | Descriptive name for the key |
| scopes | array | Array of scope strings (see Scopes & Permissions). Defaults to full access (["*"]) if omitted. |
| environment | string | live or test (default: live) |
| expires_at | string | ISO 8601 expiry datetime (optional) |
curl -X POST https://salesbooth.com/api/v1/keys \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline",
"scopes": ["deals:read", "customers:write"],
"environment": "test"
}'{
"error": false,
"data": {
"id": "key_xxxxx",
"secret": "sb_test_xxxxxxxxxxxxxxxxxxxxxxxx",
"name": "CI/CD Pipeline",
"scopes": ["deals:read", "customers:write"]
}
}curl -X PATCH "https://salesbooth.com/api/v1/keys?id=key_xxxxx" \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Key", "scopes": ["deals:read", "deals:write"] }'401 INVALID_KEY.curl -X DELETE "https://salesbooth.com/api/v1/keys?id=key_xxxxx" \
-H "Cookie: session=..."Sandbox
Test your integration without affecting live data. Sandbox endpoints require a test API key (sb_test_*) or sandbox mode enabled in the dashboard. All sandbox data is isolated from production.
Test keys only. All sandbox endpoints return 403 SANDBOX_ONLY when called with a live API key (sb_live_*). Generate a test key from the API Keys page in your dashboard.
curl -X POST https://salesbooth.com/api/v1/sandbox/reset \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"action": "reset",
"environment": "test",
"sandbox": true,
"deleted": {
"deals": 10,
"customers": 5,
"products": 10,
"contracts": 1
},
"total_deleted": 26,
"message": "Cleared 26 test records across 4 tables."
}
}curl -X POST https://salesbooth.com/api/v1/sandbox/seed \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"action": "seed",
"environment": "test",
"sandbox": true,
"seeded": {
"customers": 5,
"products": 10,
"deals": 10,
"deal_line_items": 22,
"contracts": 1
},
"total_seeded": 48,
"message": "Created 48 test records: 5 customers, 10 products, 10 deals, 22 line items, 1 contracts."
}
}| Field | Description |
|---|---|
| event required | Event type to simulate (e.g. deal.created, deal.closed, payment.received) |
| data | Custom payload object. If omitted, a realistic synthetic payload is generated automatically. |
curl -X POST https://salesbooth.com/api/v1/sandbox/simulate_webhook \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"event": "deal.closed",
"data": {
"deal_id": "deal_test_abc123",
"total": "2500.00",
"currency": "USD"
}
}'{
"error": false,
"data": {
"action": "simulate_webhook",
"environment": "test",
"sandbox": true,
"event": "deal.closed",
"deliveries": 2,
"webhook_ids": ["wh_xxxxx", "wh_yyyyy"],
"message": "Dispatched 'deal.closed' event to 2 webhook(s)."
}
}curl https://salesbooth.com/api/v1/sandbox/status \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"environment": "test",
"sandbox": true,
"counts": {
"deals": 10,
"customers": 5,
"products": 10,
"contracts": 1,
"deal_escrow": 0
},
"total_test_records": 26
}
}Playground API
The Playground API provisions ephemeral sandbox keys for the interactive API playground. Keys are auto-generated with limited scopes and expire after 2 hours. No authentication required — playground sessions are inherently sandboxed. Rate limited to 10 sessions per hour per IP.
sb_test_* API key with sandbox scopes that expires in 2 hours. Sandbox data is automatically seeded on first use. No authentication required.curl -X POST https://salesbooth.com/api/v1/playground/session \
-H "Content-Type: application/json"{
"error": false,
"data": {
"api_key": "sb_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"key_prefix": "sb_test_xxxx",
"expires_at": "2026-03-24T12:00:00Z",
"expires_in": 7200,
"environment": "test",
"sandbox": true,
"scopes": ["deals:read", "deals:write", "customers:read", "customers:write", "products:read", "contracts:read", "webhooks:read", "sandbox:read", "sandbox:write"],
"seeded": true
}
}| Parameter | Type | Description |
|---|---|---|
| key_prefix required | string | First 12 characters of the playground API key (min 8 chars) |
curl "https://salesbooth.com/api/v1/playground/session?key_prefix=sb_test_xxxx"{
"error": false,
"data": {
"valid": true,
"expires_at": "2026-03-24T12:00:00Z",
"reason": null
}
}Widgets
Manage embedded deal widgets programmatically. Widgets are configured per-site with auto-generated publishable API keys and can be created manually or from deal templates.
api_key.| Parameter | Description |
|---|---|
| id | Widget ID to retrieve a specific widget. When omitted, returns the full list for the authenticated tenant. |
| api_key | Publishable API key (sb_pub_*) for public widget config access. No authentication required when using this parameter — this is the primary mechanism for embedded widgets to fetch their configuration client-side. |
curl https://salesbooth.com/api/v1/widget-config \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/widget-config?api_key=sb_pub_xxxxxxxxxxxx"{
"error": false,
"data": {
"widget_id": "wc_xxxxxxxxxxxx",
"title": "Get Started",
"theme_color": "#2563eb",
"currency": "USD",
"merchant_available": true,
"stripe_publishable_key": "pk_live_xxxxxxxxxxxx",
"products": ["prod_xxxxxxxxxxxx"],
"steps": ["products", "customer", "payment"]
}
}| Parameter | Description |
|---|---|
| id required | Widget ID to retrieve |
| api_key | Publishable API key (sb_pub_*) — use instead of Authorization header for public/client-side access |
curl "https://salesbooth.com/api/v1/widget-config?id=wc_xxxxxxxxxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"widget_id": "wc_xxxxxxxxxxxx",
"site_id": "site_xxxxxxxxxxxx",
"title": "Get Started",
"theme_color": "#2563eb",
"currency": "USD",
"products": ["prod_xxxxxxxxxxxx"],
"steps": ["products", "customer", "payment"],
"api_key": "sb_pub_xxxxxxxxxxxx",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z"
}
}| Field | Description |
|---|---|
| site_id required | The site to attach the widget to |
| title | Widget title (default: “Create a Deal”) |
| theme_color | Hex colour code (default: #2563eb) |
| currency | ISO currency code (default: USD) |
| products | Comma-separated product IDs |
| Field | Description |
|---|---|
| title | Widget title |
| theme_color | Hex colour code |
| currency | ISO currency code |
| products | Comma-separated product IDs |
| cta_text | Call-to-action button label |
| enable_negotiation | Boolean — enable buyer negotiation |
| locale | BCP 47 locale code (e.g. en-AU) |
| dark_mode | Boolean — enable dark theme |
| custom_css | Custom CSS string injected into the widget |
curl -X PATCH "https://salesbooth.com/api/v1/widget-config?id=wgt_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"theme_color": "#10b981",
"cta_text": "Get a Quote",
"enable_negotiation": true
}'curl -X DELETE "https://salesbooth.com/api/v1/widget-config?id=wgt_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Description |
|---|---|
| template_id required | Deal template identifier (e.g. dtpl_xxxxx) |
| site_id | Site to associate with the generated config |
curl -X POST https://salesbooth.com/api/v1/widget-config/from-template \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "template_id": "dtpl_abc123", "site_id": "site_xyz" }'{
"error": false,
"success": true,
"data": {
"title": "Enterprise Starter Pack",
"products": "prod_1, prod_2",
"currency": "USD",
"deal_template_id": "dtpl_abc123",
"theme_color": "#2563eb",
"cta_text": "Get Started"
}
}| Field | Type | Description |
|---|---|---|
| product_ids required | array | Array of product ID strings to base the configuration on |
curl -X POST "https://salesbooth.com/api/v1/widget-config/auto-configure" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "product_ids": ["prod_aaa", "prod_bbb"] }'{
"error": false,
"data": {
"products": "prod_aaa, prod_bbb",
"currency": "USD",
"locale": "en",
"theme_color": "#2563eb",
"show_negotiation": true,
"show_contract": false
}
}<script> tag with SRI integrity attributes and the <salesbooth-deal> custom element. Also accessible as GET /api/v1/widget-config?id={id}&action=embed-code.| Parameter | Description |
|---|---|
| id required | Widget identifier |
curl "https://salesbooth.com/api/v1/widget-config/embed-code?id=wgt_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"html": "<!-- Salesbooth Deal Widget -->\n<script src=\"...\" integrity=\"sha384-...\" crossorigin=\"anonymous\"></script>\n<salesbooth-deal api-key=\"sb_pub_xxxxx\"></salesbooth-deal>",
"integrity": "sha384-...",
"sdk_url": "https://salesbooth.com/sdk/v1/salesbooth-widget.js"
}
}SDK Usage
const sb = new Salesbooth({ apiKey: 'sb_test_example_key_do_not_use' });
// List all widgets
const { widgets } = await sb.widgets.list();
// Create a widget from a deal template
const config = await sb.widgets.createFromTemplate('dtpl_abc123', 'site_xyz', {
theme_color: '#10b981'
});
// Get the embed code for a widget
const { html } = await sb.widgets.getEmbedCode('wgt_xxxxx');
document.getElementById('widget-container').innerHTML = html;Saved Configurations
Save product configuration snapshots with shareable short codes. Buyers can build a configuration in the widget, save it, share it via URL, and later convert it into a deal. Authenticated with a publishable key (sb_pub_*) via Bearer header.
Rate limits. Saves are limited to 10 per hour per IP. Reads are limited to 30 per minute per IP. Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) are included in every response.
Short Codes
Each saved configuration receives a unique short code — an 8–16 character hex string (e.g. a3f8b21c). Short codes are used to retrieve and share configurations via URLs like https://salesbooth.com/configure/a3f8b21c.
Configurations expire after 30 days by default (configurable via expires_in, 1–365 days). Widget-level TTL defaults can be set in the widget configuration.
| Field | Description |
|---|---|
| product_selections required | Array of product selections with product_id, name, quantity, and configured_price |
| pricing_snapshot required | Pricing breakdown object (subtotals, discounts, taxes at time of configuration) |
| option_selections | Object mapping product IDs to their selected option values |
| subtotal | Total price as a number |
| currency | ISO 4217 currency code (default: USD) |
| customer_name | Buyer name (optional) |
| customer_email | Buyer email (optional) |
| expires_in | Expiration in days, 1–365 (default: 30) |
curl -X POST https://salesbooth.com/api/v1/saved-configs \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"product_selections": [
{
"product_id": "prod_abc123",
"name": "Pro Plan",
"quantity": 1,
"configured_price": 99.00
}
],
"pricing_snapshot": {
"subtotal": 99.00,
"tax": 8.17,
"total": 107.17
},
"subtotal": 99.00,
"currency": "USD",
"customer_email": "buyer@example.com",
"expires_in": 14
}'{
"error": false,
"success": true,
"data": {
"short_code": "a3f8b21c",
"share_url": "https://salesbooth.com/configure/a3f8b21c",
"expires_at": "2026-03-22 14:30:00",
"created_at": "2026-03-08 14:30:00"
}
}price_changes array will list the differences.| Parameter | Description |
|---|---|
| code required | The 8–16 character hex short code |
curl https://salesbooth.com/api/v1/saved-configs?code=a3f8b21c \
-H "Authorization: Bearer sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"short_code": "a3f8b21c",
"widget_id": "wgt_xxxxx",
"product_selections": [...],
"pricing_snapshot": {...},
"subtotal": 99.00,
"currency": "USD",
"expired": false,
"expires_at": "2026-03-22 14:30:00",
"share_url": "https://salesbooth.com/configure/a3f8b21c",
"price_changes": []
}
}sb_pub_*).| Field | Description |
|---|---|
| customer_name | Buyer name |
| customer_email | Buyer email address |
| option_selections | Object mapping product IDs to their selected option values |
curl -X PATCH "https://salesbooth.com/api/v1/saved-configs?id=a3f8b21c" \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Jane Smith",
"customer_email": "jane@example.com"
}'sb_pub_*).curl -X DELETE "https://salesbooth.com/api/v1/saved-configs?id=a3f8b21c" \
-H "Authorization: Bearer sb_pub_xxxxx"409 if already converted, 410 if expired.| Field | Description |
|---|---|
| short_code required | The short code of the saved configuration |
| customer_name required | Buyer name (overrides saved value if provided) |
| customer_email required | Buyer email (overrides saved value if provided) |
| customer_phone | Buyer phone number |
curl -X POST https://salesbooth.com/api/v1/saved-configs?action=to_deal \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"short_code": "a3f8b21c",
"customer_name": "Jane Smith",
"customer_email": "jane@example.com"
}'{
"error": false,
"success": true,
"data": {
"deal_id": "deal_xxxxx",
"deal": { ... },
"converted_from": "a3f8b21c"
}
}Widget Analytics
Track widget conversion funnels and user engagement. Record events from embedded widgets and retrieve aggregated metrics.
| Field | Type | Description |
|---|---|---|
| api_key required | string | Widget publishable API key |
| session_id required | string | Unique session identifier |
| event_type required | string | Event type: view, step, submit, error |
| step | string | Current step name |
| data | object | Additional event data |
curl -X POST https://salesbooth.com/api/v1/widget-analytics \
-H "Content-Type: application/json" \
-d '{
"api_key": "sb_pub_xxxxx",
"session_id": "sess_abc123",
"event_type": "step",
"step": "product_selection"
}'| Parameter | Type | Description |
|---|---|---|
| widget_id required | string | Widget identifier |
| days | integer | Lookback period in days (default: 30) |
curl https://salesbooth.com/api/v1/widget-analytics?widget_id=wgt_xxxxx&days=7 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Uploads
Upload image files for products, option swatches, and other assets.
curl -X POST https://salesbooth.com/api/v1/uploads \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-F "file=@product-image.jpg"{
"error": false,
"data": {
"url": "https://salesbooth.com/uploads/img_abc123.jpg",
"filename": "product-image.jpg",
"size": 145280,
"mime_type": "image/jpeg"
}
}Credits
Prepaid credit balance management. Check balance, top up via Stripe Checkout, view ledger history, and estimate deal costs.
curl https://salesbooth.com/api/v1/credits \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"balance": 5000,
"currency": "credits"
}
}| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max results (default: 50) |
| offset | integer | Pagination offset |
| type | string | Filter: credit or debit |
curl https://salesbooth.com/api/v1/credits?action=ledger&limit=10 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| amount required | number | Number of credits to purchase |
| success_url | string | Redirect URL on success |
| cancel_url | string | Redirect URL on cancel |
curl -X POST https://salesbooth.com/api/v1/credits?action=topup \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "amount": 1000 }'| Field | Type | Description |
|---|---|---|
| deal_id | string | Deal to estimate cost for |
curl -X POST https://salesbooth.com/api/v1/credits?action=estimate \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123" }'Billing
Billing dashboard endpoints for credit balance, auto top-up configuration, usage charts, and alert settings.
curl https://salesbooth.com/api/v1/billing \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| days | integer | Lookback period in days (default: 30) |
curl https://salesbooth.com/api/v1/billing?action=usage-daily&days=14 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| enabled required | boolean | Enable or disable auto top-up |
| threshold | integer | Trigger when balance drops below this |
| amount | integer | Credits to purchase on trigger |
curl -X POST https://salesbooth.com/api/v1/billing?action=auto-topup \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "enabled": true, "threshold": 100, "amount": 500 }'curl -X POST https://salesbooth.com/api/v1/billing?action=alert-settings \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "low_balance_threshold": 50, "notify_email": true }'Team
Manage team members and roles. Invite users, update roles, and remove team members.
curl https://salesbooth.com/api/v1/team \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| email required | string | Email address to invite |
| role required | string | admin, member, or viewer |
curl -X POST https://salesbooth.com/api/v1/team \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "email": "colleague@example.com", "role": "member" }'| Field | Type | Description |
|---|---|---|
| membership_id required | integer | Team membership identifier |
| role required | string | New role: admin, member, or viewer |
curl -X PATCH https://salesbooth.com/api/v1/team \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "membership_id": "tm_xxxxx", "role": "admin" }'curl -X DELETE "https://salesbooth.com/api/v1/team?id=tm_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Trust Levels
View your tenant’s trust status, progress metrics, auto top-up ceilings, and level change history.
curl https://salesbooth.com/api/v1/trust \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": {
"level": "established",
"score": 85,
"auto_topup_ceiling": 10000,
"next_level": "premium",
"next_level_score": 90
}
}| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max results (default: 50) |
| offset | integer | Pagination offset |
curl https://salesbooth.com/api/v1/trust?action=history \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://salesbooth.com/api/v1/trust/revocations"{
"error": false,
"data": {
"revocations": [
{
"credential_id": "cred_abc123",
"revoked_at": "2026-02-10T08:00:00Z",
"reason": "trust_demotion"
}
],
"updated_at": "2026-03-18T12:00:00Z"
}
}Sites
Create and manage tenant websites. Each site can have multiple pages and AI-generated content. Sites are used for deal landing pages, product showcases, and customer portals.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific site |
curl https://salesbooth.com/api/v1/sites \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| domain required | string | Domain for the site (subdomain or custom domain) |
| name | string | Site display name |
| status | string | active, inactive, or coming_soon |
| settings | object | Optional site settings object |
curl -X POST https://salesbooth.com/api/v1/sites \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Enterprise Portal",
"domain": "acme.salesbooth.com",
"status": "coming_soon"
}'curl -X PATCH "https://salesbooth.com/api/v1/sites?id=site_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "status": "active" }'curl -X DELETE "https://salesbooth.com/api/v1/sites?id=site_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Site Pages
Create and manage individual pages within a site. Pages contain HTML content generated by AI or edited manually.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific page |
| site_id | string | Filter pages by site |
curl "https://salesbooth.com/api/v1/site-pages?site_id=site_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| site_id required | string | The parent site |
| title required | string | Page title |
| slug | string | URL slug (auto-generated from title if omitted) |
| content | string | Page HTML content |
| status | string | draft or published |
curl -X POST https://salesbooth.com/api/v1/site-pages \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"site_id": "site_xxxxx",
"title": "Enterprise Pricing",
"slug": "pricing",
"status": "published"
}'curl -X PATCH "https://salesbooth.com/api/v1/site-pages?id=page_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "status": "published", "title": "Updated Pricing Page" }'curl -X DELETE "https://salesbooth.com/api/v1/site-pages?id=page_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Site Suggestions
AI-driven optimization recommendations for your sites. The system analyzes session replays and conversion data to generate actionable suggestions. Accept or reject suggestions to apply or dismiss them, with rollback support.
insights=1 for acceptance rates and impact benchmarks, or health_score=1&site_id=x for a site’s optimization health score.| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific suggestion |
| site_id | string | Filter by site |
| status | string | Filter: pending, accepted, rejected |
| insights | string | Set to 1 to get learning insights (acceptance rates, impact benchmarks) |
curl "https://salesbooth.com/api/v1/site-suggestions?site_id=site_xxxxx&status=pending" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| action required | string | accept, reject, or rollback |
curl -X PATCH "https://salesbooth.com/api/v1/site-suggestions?id=sugg_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "action": "accept" }'| Field | Type | Description |
|---|---|---|
| action required | string | batch, settings, analyze, export, or ab_test |
| suggestion_ids | array | For batch: array of suggestion IDs |
| batch_action | string | For batch: accept or reject |
curl -X POST https://salesbooth.com/api/v1/site-suggestions \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"action": "batch",
"suggestion_ids": ["sugg_aaa", "sugg_bbb"],
"batch_action": "accept"
}'Plugins
Browse the plugin marketplace and manage installed plugins. Plugins extend platform functionality with features like analytics, website builder, channel integrations, and more.
| Parameter | Type | Description |
|---|---|---|
| installed | boolean | Set to 1 to list only installed plugins |
curl https://salesbooth.com/api/v1/plugins?installed=1 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"data": [
{
"id": "analytics",
"name": "Analytics",
"description": "Session replays, conversion funnels, and heatmaps",
"installed": true,
"version": "1.2.0"
}
]
}| Field | Type | Description |
|---|---|---|
| plugin_id required | string | Plugin identifier from the marketplace listing |
curl -X POST https://salesbooth.com/api/v1/plugins \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "plugin_id": "analytics" }'curl -X DELETE "https://salesbooth.com/api/v1/plugins?id=analytics" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"GDPR Compliance
Endpoints for GDPR data subject rights: right of access (Art. 15), data portability (Art. 20), right to erasure (Art. 17), consent management, and compliance monitoring. All actions are logged in the audit trail.
Anonymisation, not deletion. Financial records (deals, contracts, payments) are preserved for legal and accounting requirements but anonymised with a REDACTED-{hash} scheme. Customer PII is fully erased. The audit trail records every erasure action for compliance verification.
Required Scopes
| customers:read | Required for data export, consent retrieval, erasure verification, dashboard, and processing register |
| customers:write | Required for erasure, consent recording, consent withdrawal, and retention policy updates |
Data Subject Rights
Endpoints supporting data subject access requests (DSARs) and the right to be forgotten.
| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
curl "https://salesbooth.com/api/v1/gdpr/export?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"REDACTED-{hash} to preserve accounting integrity. Does not propagate to external systems — use /gdpr/full_erase for cross-system erasure.| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| Body Field | Type | Description |
|---|---|---|
| reason | string | Reason for erasure (logged in audit trail, default: “GDPR erasure request”) |
curl -X DELETE "https://salesbooth.com/api/v1/gdpr/erase?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "Customer requested data deletion"}'| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| Body Field | Type | Description |
|---|---|---|
| reason | string | Reason for erasure (default: “GDPR full erasure request”) |
curl -X DELETE "https://salesbooth.com/api/v1/gdpr/full_erase?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "Customer right to be forgotten request"}'| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier to verify |
curl "https://salesbooth.com/api/v1/gdpr/verify?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Consent Management
Record, retrieve, withdraw, and renew consent for specific processing purposes.
| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| active_only | string | Set to true to return only active consents |
curl "https://salesbooth.com/api/v1/gdpr/consent?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| purpose required | string | Processing purpose (e.g. marketing, analytics, necessary) |
| channel | string | How consent was collected (default: api) |
| metadata | object | Additional context about the consent |
| consent_version | string | Version of the consent policy |
curl -X POST https://salesbooth.com/api/v1/gdpr/consent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_xxxxx",
"purpose": "marketing",
"channel": "web_form",
"consent_version": "2.0"
}'| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| Body Field | Type | Description |
|---|---|---|
| purpose required | string | The consent purpose to withdraw |
curl -X DELETE "https://salesbooth.com/api/v1/gdpr/consent?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"purpose": "marketing"}'| Field | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| reason | string | Reason for withdrawal (default: “Batch consent withdrawal”) |
curl -X POST https://salesbooth.com/api/v1/gdpr/consent_withdraw_all \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"customer_id": "cust_xxxxx", "reason": "Customer opted out"}'| Field | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| purpose required | string | The consent purpose to renew |
| channel | string | Renewal channel (default: api) |
| expiration_months | integer | Custom expiration period in months (uses purpose default if omitted) |
curl -X POST https://salesbooth.com/api/v1/gdpr/consent_renew \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"customer_id": "cust_xxxxx", "purpose": "marketing", "expiration_months": 12}'| Field | Type | Description |
|---|---|---|
| customer_id required | string | The customer identifier |
| channel | string | Renewal channel (default: api) |
curl -X POST https://salesbooth.com/api/v1/gdpr/consent_renew_all \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"customer_id": "cust_xxxxx", "channel": "re-consent-campaign"}'| Parameter | Type | Description |
|---|---|---|
| days_ahead | integer | Look-ahead window in days (default: 30, max: 365) |
| limit | integer | Maximum records to return (default: 100, max: 500) |
curl "https://salesbooth.com/api/v1/gdpr/consent_expiring?days_ahead=14" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/gdpr/consent_health \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/gdpr/consent_expiration_config \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| purpose required | string | The consent purpose to configure |
| expiration_months required | integer | Expiration period in months (1–120) |
curl -X POST https://salesbooth.com/api/v1/gdpr/consent_expiration_config \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"purpose": "marketing", "expiration_months": 12}'curl https://salesbooth.com/api/v1/gdpr/consent_purposes \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Retention Policies
View and manage data retention policies. Retention policies control how long different types of data are kept before automatic cleanup.
curl https://salesbooth.com/api/v1/gdpr/retention \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| data_type required | string | The data type to configure retention for |
| retention_days required | integer | Number of days to retain data |
| is_active | boolean | Whether the policy is active (default: true) |
curl -X PATCH https://salesbooth.com/api/v1/gdpr/retention \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"data_type": "audit_logs",
"retention_days": 730,
"is_active": true
}'Compliance Monitoring
Dashboards, reports, and audit evidence for GDPR compliance oversight.
curl https://salesbooth.com/api/v1/gdpr/dashboard \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://salesbooth.com/api/v1/gdpr/register \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| offset | integer | Number of entries to skip (default: 0) |
| limit | integer | Maximum entries to return (default: 50, max: 100) |
curl "https://salesbooth.com/api/v1/gdpr/erasure_log?limit=25" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| q required | string | Search query (name, email, or customer ID) |
| limit | integer | Maximum results (default: 10, max: 20) |
curl "https://salesbooth.com/api/v1/gdpr/search?q=jane%40example.com" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| date_from | string | Start of date range, YYYY-MM-DD (defaults to 30 days ago) |
| date_to | string | End of date range, YYYY-MM-DD (defaults to today) |
curl "https://salesbooth.com/api/v1/gdpr/audit_evidence?date_from=2026-01-01&date_to=2026-03-31" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Compliance & GDPR Flows
Step-by-step consent lifecycle and data subject request (DSAR) flows with complete curl examples. All compliance actions are logged in the immutable audit trail for regulatory evidence.
Consent Lifecycle
Consent follows a four-step lifecycle: record → check → withdraw → renew. All steps are idempotent and generate audit trail entries.
Step 1 — Record consent at sign-up
curl -X POST https://salesbooth.com/api/v1/gdpr/consent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_xxxxx",
"purpose": "marketing_email",
"channel": "web_form",
"consent_version": "2.1",
"metadata": { "ip": "1.2.3.4", "form": "signup" }
}'Step 2 — Check active consents before sending
curl "https://salesbooth.com/api/v1/gdpr/consent?customer_id=cust_xxxxx&active_only=true" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"
# Response includes:
# { "consents": [{ "purpose": "marketing_email", "status": "active",
# "expires_at": "2028-03-01", ... }],
# "valid_purposes": ["marketing_email", "marketing_sms", ...] }Step 3 — Withdraw consent on unsubscribe
curl -X DELETE "https://salesbooth.com/api/v1/gdpr/consent?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"purpose": "marketing_email"}'Step 4 — Renew consent before expiry
curl -X POST https://salesbooth.com/api/v1/gdpr/consent_renew \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_xxxxx",
"purpose": "marketing_email",
"channel": "re-consent-email",
"expiration_months": 24
}'Data Export Flow (Art. 15/20)
Export all data held about a customer in portable JSON format. The export is logged in the audit trail.
# Request export
curl "https://salesbooth.com/api/v1/gdpr/export?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"
# Response shape:
# { "export": {
# "customer": { "customer_id": "cust_xxxxx", "name": "...", "email": "...", ... },
# "deals": [...],
# "contracts": [...],
# "consents": [...],
# "audit_trail": [...],
# "exported_at": "2026-03-18T10:00:00+00:00"
# }
# }Erasure Flow (Art. 17)
Erase a customer’s PII. Financial records are anonymised (not deleted) to preserve accounting integrity. Use /gdpr/verify to confirm erasure completion.
# Request erasure
curl -X DELETE "https://salesbooth.com/api/v1/gdpr/full_erase?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"reason": "Customer exercised right to be forgotten"}'
# Verify erasure
curl "https://salesbooth.com/api/v1/gdpr/verify?customer_id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"
# Verify response includes a verification_hash for the audit record:
# { "verification": {
# "erased": true,
# "tables_checked": 12,
# "pii_remaining": 0,
# "verification_hash": "sha256:a1b2c3..."
# }
# }Audit Trail Integrity
Every audit entry is linked to the previous one via a SHA-256 hash chain, forming a tamper-evident log. The hash formula is:
entry_hash = SHA256(
entity_type + entity_id + action
+ changes_json + snapshot_json
+ previous_hash <-- links to previous entry
+ created_at
)Any attempt to modify or delete an entry breaks the chain. The GET /api/v1/audit?action=verify&entity_type=customer&entity_id=cust_xxxxx endpoint re-computes the chain and returns a status of intact or tampered.
Compliance Evidence
Enterprise procurement and legal teams require structured evidence of GDPR compliance before approving software vendors. The compliance evidence endpoint returns a point-in-time package covering all key compliance dimensions.
customers:readcurl https://salesbooth.com/api/v1/gdpr/compliance_evidence \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"evidence": {
"report_type": "compliance_evidence",
"generated_at": "2026-03-18T10:00:00+00:00",
"tenant_id": "tenant_xxxxx",
"retention_policies": {
"policies": [
{ "data_type": "audit_logs", "retention_days": 730,
"is_active": true, "compliance_status": "active" }
],
"overall_compliant": true,
"policy_count": 5
},
"consent_statistics": {
"by_purpose": {
"marketing_email": {
"granted": 1420, "withdrawn": 83, "expired": 12,
"expiring_soon": 47, "total": 1562
},
"analytics": {
"granted": 1380, "withdrawn": 31, "expired": 8,
"expiring_soon": 22, "total": 1441
}
},
"purpose_count": 6
},
"erasure_log": {
"recent_entries": [
{ "log_id": "audit_abc123", "entity_id": "cust_xxxxx",
"action": "erased", "actor_type": "api_key",
"entry_hash": "sha256:a1b2c3...", "created_at": "2026-03-15 09:12:00" }
],
"entry_count": 12
},
"audit_integrity": {
"status": "verified", // "verified" | "broken" | "unavailable"
"checked_entries": 100,
"broken_entries": 0,
"sample_size": 100
},
"data_processing_register": {
"total_entries": 8,
"data_categories": ["contact_data", "financial_data", "usage_data"],
"legal_bases": ["consent", "contract", "legitimate_interest"]
}
}
}
}Enterprise tip: Run this endpoint quarterly and store the response as a compliance snapshot. The audit_integrity.status field is especially useful for demonstrating tamper-evidence to auditors. A broken status triggers an immediate security review.
Webhook Signature Verification
Every webhook delivery includes two headers: X-Salesbooth-Signature (HMAC-SHA256 of the timestamp + raw body) and X-Salesbooth-Timestamp (Unix timestamp of delivery). Verify both before processing any webhook payload to prevent spoofed and replayed requests.
Algorithm: v1= + HMAC-SHA256(webhook_secret, timestamp + "." + raw_request_body). The webhook secret is per-webhook and is set at creation time. Never log or expose the secret. Rotate it from the webhook settings page if compromised.
Verification — PHP
<?php
define('WEBHOOK_SECRET', getenv('SALESBOOTH_WEBHOOK_SECRET'));
define('TOLERANCE_SECONDS', 300);
// Read the raw request body (must be read BEFORE parsing JSON)
$rawBody = file_get_contents('php://input');
// Get the signature and timestamp headers
$signature = $_SERVER['HTTP_X_SALESBOOTH_SIGNATURE'] ?? '';
$ts = $_SERVER['HTTP_X_SALESBOOTH_TIMESTAMP'] ?? '';
// Replay protection: reject events older than 5 minutes
if (abs(time() - (int) $ts) > TOLERANCE_SECONDS) {
http_response_code(400);
exit(json_encode(['error' => 'Timestamp out of tolerance']));
}
// Compute expected signature: v1= + HMAC-SHA256(timestamp.body, secret)
$signedContent = $ts . '.' . $rawBody;
$expected = 'v1=' . hash_hmac('sha256', $signedContent, WEBHOOK_SECRET);
// Constant-time comparison to prevent timing attacks
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit(json_encode(['error' => 'Invalid signature']));
}
// Safe to parse and process the payload
$event = json_decode($rawBody, true);
$eventType = $event['event']; // e.g. "deal.closed"
$data = $event['data'];
switch ($eventType) {
case 'deal.closed':
// Handle deal closure
break;
case 'customer.deleted':
// Handle GDPR erasure notification
break;
}Verification — Python
import hmac
import hashlib
import os
import time
from flask import Flask, request, abort
import json
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['SALESBOOTH_WEBHOOK_SECRET']
TOLERANCE_SECONDS = 300
@app.route('/webhook', methods=['POST'])
def webhook():
# Read raw body before any parsing
raw_body = request.get_data()
# Get signature and timestamp headers
signature = request.headers.get('X-Salesbooth-Signature', '')
ts = request.headers.get('X-Salesbooth-Timestamp', '0')
# Replay protection: reject events older than 5 minutes
if abs(time.time() - int(ts)) > TOLERANCE_SECONDS:
abort(400, 'Timestamp out of tolerance')
# Compute expected signature: v1= + HMAC-SHA256(timestamp.body, secret)
signed_content = f"{ts}.".encode('utf-8') + raw_body
expected = 'v1=' + hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
signed_content,
hashlib.sha256
).hexdigest()
# Constant-time comparison
if not hmac.compare_digest(expected, signature):
abort(401, 'Invalid signature')
event = json.loads(raw_body)
event_type = event.get('event') # e.g. "deal.closed"
data = event.get('data')
if event_type == 'deal.closed':
handle_deal_closed(data)
elif event_type == 'customer.deleted':
handle_customer_deletion(data)
return '', 200Verification — JavaScript (Node.js)
import express from 'express';
import crypto from 'crypto';
const app = express();
const WEBHOOK_SECRET = process.env.SALESBOOTH_WEBHOOK_SECRET;
const TOLERANCE_SECONDS = 300;
// IMPORTANT: Use express.raw() to get the raw body before any JSON parsing
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body; // Buffer from express.raw()
const signature = req.headers['x-salesbooth-signature'] || '';
const timestamp = req.headers['x-salesbooth-timestamp'] || '0';
// Replay protection: reject events older than 5 minutes
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (age > TOLERANCE_SECONDS) {
return res.status(400).json({ error: 'Timestamp out of tolerance' });
}
// Compute expected signature: v1= + HMAC-SHA256(timestamp.body, secret)
const signedContent = `${timestamp}.${rawBody.toString('utf8')}`;
const expected = 'v1=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(signedContent, 'utf8')
.digest('hex');
// Constant-time comparison to prevent timing attacks
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(rawBody.toString());
const { event: eventType, data } = event;
switch (eventType) {
case 'deal.closed':
handleDealClosed(data);
break;
case 'customer.deleted':
handleCustomerDeleted(data);
break;
}
res.status(200).end();
});Security notes:
- Always read the raw body before any JSON parsing — even whitespace differences will invalidate the signature.
- Include both the timestamp and body in the signed content (
timestamp + "." + raw_body) and prefix the result withv1=. - Reject stale events where
X-Salesbooth-Timestampis older than 300 seconds to prevent replay attacks. - Use constant-time comparison (
hash_equalsin PHP,hmac.compare_digestin Python,crypto.timingSafeEqualin Node.js) to prevent timing attacks. - Return
2xxquickly and process asynchronously — webhooks time out after 10 seconds. - Implement idempotency using
event_idto handle duplicate deliveries.
Customer Authentication
Magic-link authentication for the Customer Portal. Customers receive a time-limited login link via email and exchange it for a short-lived JWT session. No seller API key is required — this endpoint is fully public.
Rate limiting: Magic-link requests are limited to 3 per minute per email+IP to prevent enumeration. A 200 response is returned even when the email is not found.
| Field | Type | Description |
|---|---|---|
| email required | string | Customer email address |
curl -X POST https://salesbooth.com/api/v1/customer-auth?action=request-access \
-H "Content-Type: application/json" \
-d '{ "email": "customer@example.com" }'{
"error": false,
"success": true,
"data": { "message": "If this email is registered, a login link has been sent." }
}| Field | Type | Description |
|---|---|---|
| token required | string | One-time token from the magic link |
curl -X POST https://salesbooth.com/api/v1/customer-auth?action=verify \
-H "Content-Type: application/json" \
-d '{ "token": "clat_xxxxxxxxxxxxxxxxxxxxxxxx" }'{
"error": false,
"success": true,
"data": {
"access_token": "eyJ...",
"refresh_token": "clar_xxxxxxxxxxxxxxxx",
"expires_in": 3600,
"customer_id": "cust_xxxxx",
"tenant_id": "tenant_xxxxx"
}
}| Field | Type | Description |
|---|---|---|
| refresh_token required | string | Refresh token from the verify response |
Customer Portal
Authenticated self-service endpoints for buyers. All requests require a valid customer JWT issued by Customer Authentication sent as a Bearer token. Covers deal viewing, contract signing, payment, and profile management.
Authentication: Authorization: Bearer <customer_jwt> — use the JWT from the verify response, not a seller API key.
| Parameter | Type | Description |
|---|---|---|
| status | string | Filter by deal status |
| limit | integer | Max results (default: 20) |
| offset | integer | Pagination offset |
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to sign |
| signature_name required | string | Customer’s full name as signature |
| agreed required | boolean | Must be true to accept terms |
client_secret needed by Stripe.js to complete the payment on the buyer’s side.| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to pay for |
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal identifier |
| payment_intent_id required | string | Stripe PaymentIntent ID |
| Field | Type | Description |
|---|---|---|
| name | string | Display name |
| phone | string | Phone number |
| company | string | Company name |
Deal Discovery
Enables AI agents to search and browse available deal offers programmatically. Unlike the Agent Discovery admin API (merchant control plane), this endpoint is agent-facing — it returns offers that the seller has marked as agent-discoverable, filtered by the agent’s criteria.
Required scope: agent:discover
Also available via MCP: The discover_deals MCP tool exposes the same data in a structured format for Claude and other MCP-compatible agents. See MCP Protocol.
| Parameter | Type | Description |
|---|---|---|
| category | string | Filter by product category |
| min_price | number | Minimum deal price |
| max_price | number | Maximum deal price |
| currency | string | ISO 4217 currency code (e.g. USD) |
| pricing_model | string | Filter by pricing model type |
| limit | integer | Max results (default: 50, max: 100) |
| offset | integer | Pagination offset |
curl "https://salesbooth.com/api/v1/deal-discovery?category=software&max_price=5000¤cy=USD" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"deals": [
{
"deal_id": "deal_xxxxx",
"title": "Enterprise Software Suite",
"price": 2999.00,
"currency": "USD",
"category": "software",
"negotiable": true,
"terms_schema_url": "/api/v1/schema/deal-terms"
}
],
"pagination": { "total": 42, "limit": 50, "offset": 0, "count": 1 }
}
}Quotes & Invoices
Generate shareable quotes and invoices from deals. Quotes have a configurable validity window; invoices are permanent records. Both are retrievable via a short code without authentication, making them safe to share directly with customers.
Required scope: deals:read to generate quotes, deals:write to generate invoices. Short-code retrieval is unauthenticated.
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to generate quote for |
| validity_days | integer | Days until quote expires (default: 30) |
curl -X POST https://salesbooth.com/api/v1/quotes?action=generate_quote \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "validity_days": 14 }'{
"error": false,
"success": true,
"data": {
"quote_id": "qte_xxxxx",
"short_code": "q7f3a1b2",
"share_url": "https://salesbooth.com/quote/q7f3a1b2",
"expires_at": "2026-03-29T00:00:00Z",
"total": 2999.00,
"currency": "USD"
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | Closed deal to invoice |
| Parameter | Type | Description |
|---|---|---|
| short_code required | string | The short code from the generate response |
Deal Notifications
Send transactional notifications to customers about deal activity. Supports email and SMS channels based on customer contact preferences. Used by agents and automations to deliver quote links, payment reminders, and deal updates.
Required scope: customers:read (GET), deals:write (POST)
Rate limit: 10 notifications per hour per API key.
| Parameter | Type | Description |
|---|---|---|
| customer_id required | string | Customer to check |
{
"error": false,
"success": true,
"data": {
"preferred_channel": "email",
"has_email": true,
"has_phone": false,
"marketing_consent": true,
"recent_notifications": [...]
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | Related deal |
| type required | string | quote_sent, invoice_sent, payment_reminder, deal_update, terms_updated |
| message | string | Optional custom message body |
curl -X POST https://salesbooth.com/api/v1/deal-notifications \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"type": "quote_sent",
"message": "Your quote is ready — view it at https://salesbooth.com/quote/q7f3a1b2"
}'Pricing Simulation
Read-only pricing tools for agents to model bundle discounts, compare cart scenarios, and analyse historical negotiation patterns — without creating real deals. Useful for recommending the optimal product configuration to buyers.
Required scope: products:read
All simulation responses include "simulation_only": true to confirm no data was written.
| Field | Type | Description |
|---|---|---|
| items required | array | Array of { product_id, quantity, option_ids? } |
| currency | string | ISO 4217 code (default: USD) |
| promo_code | string | Optional promo code to test |
curl -X POST https://salesbooth.com/api/v1/simulate-pricing \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "product_id": "prod_aaa", "quantity": 2 },
{ "product_id": "prod_bbb", "quantity": 1 }
],
"currency": "USD",
"promo_code": "SAVE20"
}'{
"error": false,
"success": true,
"data": {
"items": [...],
"subtotal": 450.00,
"bundle_discounts": [{ "rule": "3-for-2 Bundle", "amount": 45.00 }],
"bundle_discount_total": 45.00,
"promo_discount": 81.00,
"total": 324.00,
"currency": "USD",
"savings_vs_individual": 126.00,
"simulation_only": true
}
}| Field | Type | Description |
|---|---|---|
| scenarios required | array | Array of { label, items } scenario objects |
| currency | string | ISO 4217 currency code |
| Parameter | Type | Description |
|---|---|---|
| product_id | string | Analyse patterns for a specific product |
| category | string | Analyse patterns for a product category |
| period_days | integer | Lookback window in days (1–365, default: 90) |
Real-Time Events (SSE)
Subscribe to a real-time stream of deal events via Server-Sent Events (SSE). Use this to power live dashboards, notification UIs, or trigger agent actions without polling. The stream requires authentication and emits events for all event types the key has access to.
Protocol: Server-Sent Events (text/event-stream)
Max stream duration: 60 minutes. A reconnect event is sent before the connection closes so your client can reconnect from the last event.
Heartbeat: A heartbeat comment is sent every 15 seconds to keep the connection alive through load balancers and proxies.
Accept: text/event-stream and keep the connection open to receive events as they occur.| Parameter | Type | Description |
|---|---|---|
| events | string | Comma-separated event types to subscribe to (e.g. deal.updated,negotiation.countered). Omit for all. |
| deal_id | string | Filter events to a specific deal |
| customer_id | string | Filter events for a specific customer |
| last_event_id | string | Resume from this event ID (also accepted via Last-Event-ID header) |
curl -N "https://salesbooth.com/api/v1/events/stream?events=deal.updated,deal.closed" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Accept: text/event-stream"const es = new EventSource(
'/api/v1/events/stream?events=deal.updated',
{ headers: { 'Authorization': 'Bearer sb_test_example_key_do_not_use' } }
);
es.addEventListener('deal.updated', (e) => {
const event = JSON.parse(e.data);
console.log('Deal updated:', event.deal_id);
});
es.addEventListener('reconnect', (e) => {
const { last_event_id } = JSON.parse(e.data);
// Reconnect with ?last_event_id= to resume
});event: connected
data: {"status":"connected","connection_id":"conn_xxxxx","heartbeat_interval":15}
event: deal.updated
data: {"deal_id":"deal_abc","status":"negotiating","_timestamp":"2026-03-15T10:30:00Z","_signature":"sha256=..."}
event: heartbeat
data: comment
event: reconnect
data: {"reason":"timeout","last_event_id":"seq:1234"}Widget A/B Tests
Run controlled experiments across two widget variants to optimise conversion. Traffic is split between the original widget (Variant A) and a challenger (Variant B) with configurable traffic weighting. Statistical significance is computed automatically.
Required scope: deals:read (GET), deals:write (POST/PATCH)
Public resolve endpoint: GET /api/v1/ab-tests/resolve?api_key=sb_pub_... — called by the widget loader on each page load to determine which variant to show. No secret key required.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific test with full stats |
curl https://salesbooth.com/api/v1/ab-tests \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| widget_id required | string | Source (Variant A) widget |
| name | string | Descriptive test name (auto-generated from widget title if omitted) |
| traffic_weight_b | integer | Percentage of traffic to send to Variant B (default: 50) |
| auto_promote | boolean | Auto-promote winner when significance threshold is reached |
| variant_b_title | string | Override widget title for Variant B |
| variant_b_cta_text | string | Override CTA button text for Variant B |
| variant_b_theme_color | string | Override theme colour hex for Variant B |
curl -X POST https://salesbooth.com/api/v1/ab-tests \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"widget_id": "wgt_xxxxx",
"name": "CTA Button Colour Test",
"traffic_weight_b": 50,
"auto_promote": true,
"variant_b_cta_text": "Get Your Quote Now",
"variant_b_theme_color": "#10b981"
}'| Field | Type | Description |
|---|---|---|
| status | string | running or paused |
| traffic_weight_b | integer | Adjusted traffic percentage for Variant B |
| Field | Type | Description |
|---|---|---|
| ab_test_id required | string | Test to close |
| winner_widget_id required | string | Widget ID of the winning variant |
{
"error": false,
"success": true,
"data": {
"variant": "A",
"widget_id": "wgt_xxxxx",
"ab_test_id": "abt_xxxxx",
"config": { "title": "Create a Deal", "theme_color": "#2563eb" }
}
}Widget Public Endpoints
These three endpoints are called directly by the <salesbooth-deal> widget using a publishable API key (sb_pub_*). They can also be called from custom storefront code to build headless checkout flows.
| Parameter | Type | Description |
|---|---|---|
| api_key required | string | Publishable key (sb_pub_*) |
| products | string | Comma-separated product IDs to analyse (falls back to widget config) |
curl "https://salesbooth.com/api/v1/widget-intelligence?api_key=sb_pub_xxxxx&products=prod_aaa,prod_bbb"{
"error": false,
"success": true,
"data": {
"products": {
"prod_aaa": { "rank": 1, "badge": "most_popular", "closed_deal_count": 142 },
"prod_bbb": { "rank": 2, "badge": null, "closed_deal_count": 87 }
},
"option_defaults": {
"prod_aaa": { "tier": { "value": "pro", "selection_rate": 68, "should_preselect": true } }
},
"price_context": { "avg_deal_value": 420.00, "min_deal_value": 99.00, "max_deal_value": 999.00 }
}
}| Field | Type | Description |
|---|---|---|
| api_key required | string | Publishable key (sb_pub_*) |
| deal_id required | string | Deal to generate contract for |
| customer_name | string | Buyer name for template personalisation |
| customer_email | string | Buyer email for template personalisation |
| Field | Type | Description |
|---|---|---|
| contract_id required | string | Contract from the generate response |
| deal_id required | string | Associated deal |
| signer_name required | string | Full legal name of the signer |
| signer_email required | string | Signer’s email address |
| signature_mode | string | checkbox, type, or draw |
| signature_image | string | Base64 PNG data URI for drawn signatures |
| Field | Type | Description |
|---|---|---|
| code required | string | Promo code entered by the buyer |
| subtotal required | number | Cart subtotal to calculate the discount amount against |
curl -X POST https://salesbooth.com/api/v1/widget-discount \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "code": "SAVE20", "subtotal": 450.00 }'{
"error": false,
"success": true,
"data": {
"valid": true,
"discount": {
"type": "percentage",
"value": 20,
"code": "SAVE20",
"amount": 90.00,
"description": "20% off your first purchase"
}
}
}requires / excludes) and returns any price adjustments from includes_price rules. Call this before creating a deal to surface configuration errors early. Authenticated with a publishable key via Bearer header.| Field | Type | Description |
|---|---|---|
| product_id required | string | Product to validate the configuration for |
| option_ids | string[] | Array of selected option IDs to validate against compatibility rules |
curl -X POST https://salesbooth.com/api/v1/widget-validate \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "product_id": "prod_aaa", "option_ids": ["opt_pro", "opt_annual"] }'{
"error": false,
"success": true,
"data": {
"valid": true,
"errors": [],
"warnings": [],
"price_adjustments": [
{
"rule_id": 12,
"option_id": "opt_annual",
"adjusted_price": 0.00,
"reason": "Price included with option opt_pro"
}
]
}
}| Field | Type | Description |
|---|---|---|
| product_id required | string | Product to calculate pricing for |
| option_ids | string[] | Selected option IDs — their price modifiers are included in the subtotal |
| quantity | integer | Number of units (default: 1) |
| promo_code | string | Optional promo code to factor into the discount calculation |
curl -X POST https://salesbooth.com/api/v1/widget-payment-preview \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "product_id": "prod_aaa", "option_ids": ["opt_pro"], "quantity": 1, "promo_code": "SAVE20" }'{
"error": false,
"success": true,
"data": {
"subtotal": 450.00,
"discount": 90.00,
"tax": 29.52,
"total": 389.52,
"chargeAmount": 97.38,
"isDeposit": true,
"currency": "USD"
}
}FX Rates
Retrieve real-time exchange rates between any two currencies. Used by the widget for multi-currency price display and by agents for cross-currency deal valuation. No authentication required.
Rate limit: 100 requests per hour per IP. Rates are cached server-side — use the fetched_at field to determine freshness.
| Parameter | Type | Description |
|---|---|---|
| from required | string | Source currency (ISO 4217, e.g. AUD) |
| to required | string | Target currency (ISO 4217, e.g. USD) |
curl "https://salesbooth.com/api/v1/fx-rate?from=AUD&to=USD"{
"error": false,
"success": true,
"data": {
"from": "AUD",
"to": "USD",
"rate": 0.6512,
"provider": "open-exchange",
"fetched_at": "2026-03-15T10:00:00Z"
}
}Machine-Readable Schemas
Public JSON Schema and state machine definitions for AI agents and integrators to understand deal structure programmatically. These endpoints require no authentication and are designed to be fetched and cached by agent frameworks.
No authentication required. Responses include Cache-Control headers — schemas change infrequently so aggressive caching is safe.
Rate limit: 30 requests per minute per IP.
| Parameter | Type | Description |
|---|---|---|
| entity | string | deals or contracts (omit for both) |
curl "https://salesbooth.com/api/v1/schema/state-machine?entity=deals"{
"error": false,
"success": true,
"data": {
"schema_version": "1.0.0",
"entity_types": ["deals", "contracts"],
"deals": {
"states": ["draft", "open", "negotiating", "accepted", "signed", "closed", "cancelled"],
"transitions": [
{ "from": "draft", "to": "open", "action": "publish", "conditions": ["has_product", "has_customer"] },
{ "from": "negotiating", "to": "accepted", "action": "accept", "conditions": [] }
]
}
}
}curl "https://salesbooth.com/api/v1/schema/deal-terms"{
"error": false,
"success": true,
"data": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DealTerms",
"type": "object",
"properties": {
"price": { "type": "number", "minimum": 0 },
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
"payment_terms": { "type": "string", "enum": ["immediate", "net_30", "net_60"] },
"discount_percent": { "type": "number", "minimum": 0, "maximum": 100 }
},
"required": ["price", "currency"]
}
}Object Schemas
Human-readable field definitions for the core resource objects returned by the API.
Deal Object
{
"id": "item_xxxxx",
"deal_id": "deal_xxxxx",
"product_id": "prod_xxxxx",
"quantity": 2,
"unit_price": "499.00",
"discount": "10.00",
"line_total": "888.20"
}Customer Object
Product Object
Budget Management
Configure monthly spending limits and alert thresholds for API usage. When the monthly limit is reached, the API returns 402 Payment Required until the next billing cycle or the limit is raised. Requires session (cookie) authentication.
curl https://salesbooth.com/api/v1/budget \
-H "Cookie: session=..."{
"error": false,
"success": true,
"data": {
"budget": {
"monthly_limit": 1000.00,
"monthly_limit_cents": 100000,
"alert_at_percent": [80, 100],
"current_spend": 612.40,
"current_spend_cents": 61240,
"percent_used": 61.2
}
}
}| Field | Type | Description |
|---|---|---|
| monthly_limit required | number | Monthly spend cap in USD. Set to 0 to disable the cap. |
| alert_at_percent | array | Percentage thresholds that trigger email alerts (e.g. [80, 100]) |
curl -X POST https://salesbooth.com/api/v1/budget \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{ "monthly_limit": 500.00, "alert_at_percent": [75, 90, 100] }'USDC Payment Rail
Manage USDC prepaid balances for AI agent accounts on Base L2. Agents can fund their account via Coinbase Commerce and then spend USDC against deal credits — no Stripe required. USDC balances are separate from the standard credit system.
Required scope: billing:read (balance & transactions), billing:write (create charge, set wallet)
Network: Base L2 (Coinbase). Minimum deposit: 1 USDC.
curl https://salesbooth.com/api/v1/usdc?action=balance \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"balance_usdc": 42.50,
"wallet_address": "0xabc...def",
"network": "base",
"agent_summary": { "total_spent": 157.50, "total_deposited": 200.00 }
}
}| Parameter | Type | Description |
|---|---|---|
| page | integer | Page number (default: 1) |
| per_page | integer | Results per page (default: 20, max: 100) |
| Field | Type | Description |
|---|---|---|
| amount required | number | Amount in USDC to deposit (minimum: 1) |
curl -X POST https://salesbooth.com/api/v1/usdc?action=create-charge \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "amount": 50.00 }'| Field | Type | Description |
|---|---|---|
| wallet_address required | string | EVM-compatible wallet address (0x...) |
Fraud Signals
Review and manage fraud detection signals. Signals are generated automatically during deal and payment events and accumulate a severity score that may trigger trust-level demotions. This API allows merchants to list, resolve, and manually trigger evaluations.
Required scope: trust:read (GET), trust:write (POST resolve/evaluate)
| Parameter | Type | Description |
|---|---|---|
| unresolved | string | Show only unresolved signals: 1 or 0 |
| limit | integer | Max results (default: 25, max: 100) |
| offset | integer | Pagination offset |
curl https://salesbooth.com/api/v1/fraud-signals?unresolved=1 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| signal_id required | string | Signal to resolve |
Session Auth
JSON-based authentication for the admin panel and SPA clients. Uses secure HttpOnly cookies; not intended for API key–based integrations.
authenticated: false if no active session.action=select-tenant to switch to a specific workspace after login.| Field | Type | Description |
|---|---|---|
| email required | string | User email address |
| password required | string | User password |
| remember | boolean | Persist session across browser restarts (30-day remember-me cookie) |
curl -X POST https://salesbooth.com/api/v1/auth \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"email": "user@example.com", "password": "secret"}'200 OK even if no active session exists.User Profile
Manage the authenticated user’s personal profile fields. Requires an active session.
{
"error": false,
"data": {
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"phone": "+1-555-0100"
}
}| Field | Type | Description |
|---|---|---|
| first_name | string | First name |
| last_name | string | Last name |
| phone | string | Mobile phone number |
| Field | Type | Description |
|---|---|---|
| old_password required | string | The user’s current password for verification |
| new_password required | string | The new password (minimum 8 characters) |
curl -X POST "https://salesbooth.com/api/v1/profile?action=change-password" \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{
"old_password": "oldPassword123",
"new_password": "newSecurePassword456"
}'Preferences
Store user and workspace-level UI preferences. Preferences are keyed by scope: user (global per user) or tenant (per workspace).
user scope returns global preferences (language, theme). tenant scope returns workspace preferences (recommendations_dismissed).language, theme for user scope; recommendations_dismissed for tenant scope.curl -X PATCH https://salesbooth.com/api/v1/preferences?scope=user \
-H "Content-Type: application/json" \
-d '{"language": "en", "theme": "dark"}'Display Mode
Control whether the app renders in PWA standalone mode or standard browser mode. Called automatically by the front-end on load; you can also set it explicitly for testing.
standalone, browser, or auto.auto to clear any forced mode and return to browser-detection.| Field | Type | Description |
|---|---|---|
| mode required | string | standalone, browser, or auto |
Business Profile
The business profile provides a natural-language description of the tenant’s business, used as context for AI website generation. Stored in tenant_settings.
| Field | Type | Description |
|---|---|---|
| business_name | string | Trading name |
| description | string | Detailed description of the business, products, and target market |
| industry | string | Industry category |
| target_market | string | Intended customer segment |
Analytics Settings
Manage analytics and session replay tracking preferences for the current workspace. Settings are stored in tenants.metadata.
| Field | Type | Description |
|---|---|---|
| enabled | boolean | Master toggle for analytics collection |
| record_mouse | boolean | Record mouse movements in session replays |
| block_selector | string | CSS selector for elements to exclude from recording (e.g. .pii-field) |
Tenant Settings
Read and update workspace-level settings: business details, branding, and operational configuration. Direct column fields are stored in the tenants table; extended fields (industry, tax_id, primary_color, currency) are stored in tenants.metadata.
| Field | Type | Description |
|---|---|---|
| name | string | Workspace display name |
| industry | string | Industry category |
| default_currency | string | ISO 4217 code (e.g. USD) |
| primary_color | string | Brand colour hex (e.g. #635bff) |
| default_tax_rate | string | Default tax rate as decimal (e.g. 0.10 for 10%) |
| custom_domain | string | Custom domain for the hosted website |
Communication Channels
Manage the tenant’s outbound communication settings: phone number (Twilio), sender email address, and sender name. Used when sending deal notifications and quotes.
| Field | Type | Description |
|---|---|---|
| phone_number | string | Twilio phone number in E.164 format (e.g. +15550001234) |
| from_email | string | Sender email address for outbound messages |
| from_name | string | Sender display name |
Switch Workspace
Switch the active workspace for the current session. Users with access to multiple workspaces can switch without re-authenticating.
| Field | Type | Description |
|---|---|---|
| tenant_id required | string | Target workspace ID |
Tenant Context
Manage per-tab workspace context. Allows each browser tab to operate in a different workspace without affecting other tabs. Context is stored in the session keyed by tab ID (sent via X-Tab-ID header).
X-Tab-ID header.tenant_id: null to clear the tab-specific context and fall back to the session default.| Field | Type | Description |
|---|---|---|
| tab_id required | string | Browser tab identifier |
| tenant_id | string|null | Workspace ID to bind to this tab, or null to clear |
Workspace List
Returns all workspaces accessible to the authenticated user. Multi-workspace users use this to populate the workspace switcher.
{
"error": false,
"data": [
{
"tenant_id": "au_tenant_abc123",
"name": "Acme Corp",
"slug": "acme-corp",
"logo_url": null,
"role": "owner",
"is_current": true,
"is_sandbox": false,
"region": "au",
"regional_endpoint": "https://api-au.salesbooth.com"
}
]
}Health Check
Public endpoint for monitoring and infrastructure probes. No authentication required. Rate limited to 10 requests per minute per IP.
status in the body.| Parameter | Description |
|---|---|
| type | liveness — minimal PHP-FPM alive check (use for Nginx upstream); readiness — all dependencies healthy (use for load balancer routing); omit for full report |
{
"status": "healthy",
"checks": {
"database": { "status": "ok", "latency_ms": 2 },
"redis": { "status": "ok" },
"queue": { "status": "ok", "pending": 0 }
},
"timestamp": "2026-03-18T10:00:00Z"
}Usage Reporting
Access AI service usage logs and cost summaries for the current workspace. Useful for cost allocation, auditing, and charting monthly spend trends.
| Parameter | Description |
|---|---|
| service | Filter by service name (e.g. claude, openai) |
| start_date | ISO 8601 start date |
| end_date | ISO 8601 end date |
| limit | Max results (default: 100, max: 1000) |
| offset | Pagination offset |
{date, cost, tokens} objects for the last 30 days.Cache Administration
Inspect and flush the Redis cache for the current workspace. Requires the admin scope. Cache keys are namespaced by sb:{tenant_id}: to prevent cross-tenant access.
prefix parameter to invalidate only a subset.| Parameter | Description |
|---|---|
| prefix | Cache key prefix to invalidate (e.g. products). Omit to flush all workspace keys. |
Encryption Key Rotation
Admin endpoint to check and trigger encryption key rotation for the current workspace. Re-encrypts all customer PII records using the current HKDF-derived tenant key. Requires the admin scope.
action=resume with a rotation ID to resume a specific paused rotation.Deal Participants
Manage multi-party deal collaboration. Participants are agents or humans invited to a deal with specific roles, permission scopes, and revenue-share allocations. Supported roles: primary_agent, co_agent, observer.
action=settlements to retrieve final revenue-share settlement records.| Parameter | Description |
|---|---|
| deal_id required | Deal identifier |
| action | settlements — return settlement records for a completed deal |
accept, complete). Send X-Delegation-ID header if acting on behalf of an agent.| Field | Type | Description |
|---|---|---|
| agent_id | string | Agent identifier to invite |
| role required | string | primary_agent, co_agent, or observer |
| revenue_share | number | Revenue share percentage (0–100) |
| scope | string[] | Permission scopes granted to this participant |
curl -X POST "https://salesbooth.com/api/v1/deal-participants?deal_id=deal_abc" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_xyz",
"role": "co_agent",
"revenue_share": 25,
"scope": ["deals:read", "agent:negotiate"]
}'draft or in_progress status.Negotiations Dashboard
Internal merchant-facing endpoint providing an aggregated view of active negotiations, auto-accept thresholds, and deal management actions. Requires an active session.
| Parameter | Description |
|---|---|
| view | dashboard (default) — stats + list; detail — single deal negotiation detail (requires deal_id) |
| deal_id | Deal ID when view=detail |
accept, counter, reject, update_threshold.| Parameter | Description |
|---|---|
| action required | accept — accept latest proposal; counter — send counter-offer; reject — reject; update_threshold — set auto-accept percentage |
Dashboard Statistics
Consolidated endpoint returning all dashboard data in a single request. Cached for 2 minutes. Requires deals:read scope.
{
"error": false,
"data": {
"counts": { "customers": 142, "deals": 38, "products": 24, "contracts": 17 },
"pipeline": { "total_value": 285000, "avg_deal_size": 7500, "win_rate": 0.68 },
"revenue_series": [{ "date": "2026-03-01", "total": 12400, "count": 3 }],
"attention": {
"expiring_deals": [],
"expiring_contracts": [],
"stale_deals": []
}
}
}Site Blocks
Manage reusable content blocks for the website builder. Blocks can be shared across pages within a site and referenced by slug. Requires session authentication.
| Parameter | Description |
|---|---|
| site_id | Filter blocks by site |
| id | Retrieve a single block by ID |
action=update_status to publish/unpublish a block.| Field | Type | Description |
|---|---|---|
| site_id required | string | Parent site identifier |
| name required | string | Block display name |
| type | string | Block type (e.g. hero, testimonial, cta) |
| content | string | HTML or structured content for the block |
AI Website Generation
Generate full websites, page templates, or individual pages using AI. Uses the tenant’s business profile and product catalog as context. Long-running — set client timeouts to at least 120 seconds.
| Parameter | Description |
|---|---|
| action | template — generate CSS, header, and footer only; page — generate or regenerate a single page; omit for full site generation |
Domain Availability
Check whether a domain name is available for registration. Powered by the Dreamscape API.
| Field | Type | Description |
|---|---|---|
| domain required | string | Domain name to check (e.g. example.com) |
{
"error": false,
"data": { "available": true, "price": "12.99" }
}{
"error": false,
"data": { "ip": "203.0.113.42" }
}Domain Registration
Register a domain name and associate it with the current workspace’s website. Charges are applied to the workspace credit balance.
| Field | Type | Description |
|---|---|---|
| domain required | string | Domain to register (must be verified as available first) |
| site_id | string | Site to associate the domain with |
Activity Stream (SSE)
Real-time activity feed using Server-Sent Events. Polls the activity_feed table every 2 seconds and streams new events to the client. Use the browser’s EventSource API for automatic reconnection.
id, event type, and data fields. The connection emits a heartbeat event every 30 seconds.| Parameter | Description |
|---|---|
| last_event_id | Resume from this event ID (also read from Last-Event-ID header on reconnect) |
const es = new EventSource('/api/v1/activity-stream');
es.addEventListener('activity', e => {
const event = JSON.parse(e.data);
console.log(event.type, event.entity_id);
});
es.addEventListener('heartbeat', () => console.log('alive'));Session Replays
Manage recorded session replay data. Replay events are collected via the Event Collection endpoint and stored as JSONL files. Use the Analytics Settings endpoint to configure recording preferences.
| Parameter | Description |
|---|---|
| session_id required | Session replay identifier |
Event Collection
Public endpoint that accepts session replay and analytics events from the SDK. No authentication required — uses a publishable key for workspace routing. Called automatically by the analytics SDK.
| Field | Type | Description |
|---|---|---|
| session_id required | string | Unique session identifier (generated client-side) |
| events required | array | Array of event objects with type, timestamp, and data |
| publishable_key required | string | Workspace publishable key for routing |
Job Queue
Monitor and manage the background job queue. Supports inspecting queue statistics, checking individual job status, viewing dead-lettered jobs, retrying failed jobs, and purging old records. Requires queue:read or queue:write scope.
queue:write scope.action=purge-dead-letter to purge only the dead letter queue.Agent Registry
Same-tenant agent peer discovery for multi-agent coordination. Enables agents to discover peer and child agents in the same workspace, query activity summaries, and detect product contention before starting duplicate negotiations. Requires agent:discover scope.
Privacy: Responses are summary-level only to prevent information leakage between competing agents. Agents may only see their peers and children — never ancestors in the delegation hierarchy.
| Parameter | Description |
|---|---|
| action required | list_agents — list active peer agents; agent_activity — activity summary for an agent; check_contention — check if a product is already being negotiated |
| product_id | Product ID to check for contention (when action=check_contention) |
| agent_id | Agent ID to query activity for (when action=agent_activity) |
AI Chat Assistant
Conversational AI assistant for managing customers, products, deals, and viewing business data. Supports streaming responses. Powered by Claude with extended thinking for complex queries.
stream=true.| Field | Type | Description |
|---|---|---|
| messages required | array | Chat history: array of {role: "user"|"assistant", content: string} |
| stream | boolean | Enable SSE streaming (default: false) |
| thinking | boolean | Enable extended thinking for complex queries (default: false) |
Text to Speech
Convert text to natural-sounding speech using OpenAI TTS. Supports streaming audio for low-latency playback. Requires a configured OpenAI API key.
stream=true for raw audio streaming (faster); the default returns base64-encoded audio in JSON.| Field | Type | Description |
|---|---|---|
| text required | string | Text to convert (max 4096 characters) |
| voice | string | Voice: nova (default), alloy, echo, fable, onyx, shimmer |
| stream | boolean | Stream raw MP3 bytes directly (sets Content-Type: audio/mpeg) |
Realtime Voice Session
Create ephemeral tokens for browser-side WebSocket connections to the OpenAI Realtime API. Keeps the OpenAI API key secure on the server. Tokens expire after 60 seconds.
| Field | Type | Description |
|---|---|---|
| model | string | OpenAI Realtime model (default: gpt-4o-realtime-preview) |
| voice | string | TTS voice for the realtime session (default: nova) |
{
"error": false,
"data": {
"client_secret": { "value": "ek_...", "expires_at": 1742000060 },
"session_id": "sess_abc123"
}
}Realtime Function Calls
Execute function calls originating from the OpenAI Realtime API voice assistant. The browser forwards function call requests here; the server executes them using full service-layer access and returns results.
| Field | Type | Description |
|---|---|---|
| function_name required | string | Name of the function to execute (e.g. search_customers, get_deal) |
| arguments required | object | Function arguments as a JSON object |
Realtime Usage Reporting
Client-side token usage reporting for Realtime API voice sessions. Because the browser connects directly to OpenAI via WebSocket, the server cannot observe usage; the client reports it here for billing and analytics tracking.
| Field | Type | Description |
|---|---|---|
| session_id required | string | Realtime session identifier |
| input_tokens | integer | Number of input tokens consumed |
| output_tokens | integer | Number of output tokens generated |
| duration_seconds | number | Session duration in seconds |
CSV Import
Three-step CSV import wizard for bulk-loading customers and products. Step 1 parses the file and auto-detects column mappings. Step 2 validates the mapped data and returns row-level errors. Step 3 executes the import inside a database transaction.
| Field | Type | Description |
|---|---|---|
| file required | file | CSV file to import (multipart upload) |
| entity required | string | customers or products |
Saved Configs Admin
Admin-side management of widget saved configurations (product configurations saved by buyers before checkout). Supports listing with search/filter/sort, analytics, conversion to deals, and deletion. Requires deals:read scope.
action=stats and action=analytics&period=30 for conversion funnel analytics.deals:write scope.deals:write scope.| Field | Type | Description |
|---|---|---|
| customer_name | string | Update the saved customer name (set to empty string to clear) |
| customer_email | string | Update the customer email address (must be valid email format) |
| option_selections | object | Updated option selections keyed by product ID (set to null to clear) |
curl -X PATCH "https://salesbooth.com/api/v1/saved-configs-admin?id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"customer_email": "updated@example.com",
"customer_name": "Jane Smith"
}'{ "error": false, "data": { "updated": true, "id": 42 } }deals:write scope.Sandbox Toggle
Switch the dashboard between live mode and sandbox (test) mode. Only available for session-authenticated users — API key requests cannot change the sandbox mode. Sandbox mode uses Stripe’s test keys and does not process real payments.
test (sandbox active) or live.| Field | Type | Description |
|---|---|---|
| mode required | string | test for sandbox, live for production |
Payment Intent
Create a Stripe PaymentIntent for a deal’s deposit or full amount. Called by the <salesbooth-deal> embed widget when the buyer reaches the payment step. Requires deals:write scope.
client_secret for use with Stripe.js to complete payment on the client.| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to create a PaymentIntent for |
| amount | number | Override amount in the deal’s currency (omit to use deal total) |
{
"error": false,
"data": {
"client_secret": "pi_xxx_secret_yyy",
"amount": 149900,
"currency": "usd"
}
}Onboarding
Complete the new-user onboarding wizard or create an additional workspace for an existing user. No authentication required for new users; existing users must be authenticated.
| Field | Type | Description |
|---|---|---|
| first_name required | string | User first name |
| last_name required | string | User last name |
| email required | string | User email address |
| password required | string | Password (min 8 characters) |
| business_name | string | Business or workspace name (required for human accounts) |
| industry | string | Business industry (required for human accounts) |
| services | array | List of services offered (required for human accounts). Each item: {name, description, price_min, price_max} |
| deposit_percentage | number | Default deposit percentage (0–100) |
| phone | string | Business phone number |
| website | string | Business website URL |
| country | string | Two-letter country code (default: US) |
| region | string | Data residency region: au, eu, or us |
| account_type | string | Account type: human (default) or agent. Agent accounts skip business field validation. |
Credential Verification
Public endpoint for verifying the signature and status of Salesbooth verifiable credentials. No authentication required — any third party (lenders, insurers, regulators, counterparties) can verify a credential’s authenticity.
valid: true if the credential is authentic and has not been revoked.| Field | Type | Description |
|---|---|---|
| credential required | object | The verifiable credential JSON object (as issued by Salesbooth) |
{
"error": false,
"data": {
"valid": true,
"credential_id": "vc_abc123",
"deal_id": "deal_xyz",
"issued_at": "2026-03-01T10:00:00Z",
"expires_at": "2027-03-01T10:00:00Z",
"revoked": false
}
}System Admin
System-level administration endpoints. Requires is_system_admin = true on the user account. These endpoints are not accessible to regular tenant users.
System Admin Only: All /system-admin endpoints enforce a system-admin check. Regular API keys and tenant users will receive 403 Forbidden.
| Action | Description |
|---|---|
| stats | System-wide statistics: total tenants, active users, deal volume |
| tenants | List all tenants with pagination |
| tenant | Get a specific tenant’s detail (requires id param) |
| settings | Get all system-level settings |
| credit-ledger | Get credit ledger for a specific tenant (requires id param) |
| Action | Description |
|---|---|
| impersonate | Start impersonating a tenant (for support) |
| stop-impersonate | End impersonation and restore system admin session |
| settings | Save a system-level setting |
| grant-credits | Grant free credits to a tenant |
| rotate-keys | Trigger encryption key rotation for a specific tenant |
Internal Endpoints
The following endpoints are infrastructure-facing or webhook receivers. They are listed here for completeness but are not intended for direct use by API consumers. Do not call these endpoints from client integrations.
curl https://salesbooth.com/api/v1/api{
"error": false,
"data": {
"name": "Salesbooth API",
"version": "1.0.0",
"base_url": "https://salesbooth.com/api/v1",
"documentation": "https://salesbooth.com/docs",
"openapi_spec": "https://salesbooth.com/api/openapi.json",
"endpoints": { "deals": { ... }, "customers": { ... } }
}
}report-uri CSP directive sent in all page responses. No authentication required. Accepts both application/csp-report and application/json content types. Rate limited at 30 req/min per IP.{
"csp-report": {
"document-uri": "https://salesbooth.com/deals",
"violated-directive": "script-src",
"blocked-uri": "https://evil.example.com/script.js"
}
}Webhook URL: https://salesbooth.com/api/v1/email-webhook
Content-Type: multipart/form-data (SendGrid) or application/json (Mailgun)Stripe-Signature header against STRIPE_WEBHOOK_SECRET. No authentication required — configure in your Stripe dashboard.| Header | Description |
|---|---|
| Stripe-Signature required | Stripe-provided HMAC signature for payload verification |
Webhook URL: https://salesbooth.com/api/v1/stripe-webhook
Events: payment_intent.*, invoice.*, customer.subscription.*, charge.dispute.*| Parameter | Description |
|---|---|
| action | settings (default) for configuration status; history for communication history log |
| Field | Description |
|---|---|
| action required | settings to save credentials; sms to send SMS; call to initiate voice call; email to send email |
| to | Recipient phone number (E.164 format) or email address, depending on action |
| message | Message body for SMS or email content |
| account_sid | Twilio account SID (required for action=settings) |
| auth_token | Twilio auth token (required for action=settings) |
| Parameter | Description |
|---|---|
| type | Event type: sms (inbound message), voice (call status), status (delivery callback) |
SMS Webhook URL: https://salesbooth.com/api/v1/twilio-webhook?type=sms
Voice Webhook URL: https://salesbooth.com/api/v1/twilio-webhook?type=voiceChangelog
2026-03-24 — 13 missing endpoints documented; subscription status enum corrected (#1209)
Documentation gap-fill pass resolving all endpoints present in openapi.json but missing from the docs:
- Federation — Added
GET /federation/negotiateandPOST /federation/negotiateendpoint cards with parameter tables and examples - Playground API — New
#playground-apisection documentingPOST /playground/session(create ephemeral key) andGET /playground/session(check validity) with full response examples - Widget Config — Corrected embed-code path to
GET /widget-config/embed-codeto match OpenAPI spec - Saved Configs Admin — Added missing
PATCH /saved-configs-admin?id={id}endpoint card documentingcustomer_name,customer_email, andoption_selectionsfields - Internal Endpoints — Upgraded summary table to full endpoint cards for
GET /api,POST /csp-report,POST /email-webhook,POST /stripe-webhook,GET /twilio,POST /twilio, andPOST /twilio-webhook - Subscriptions — Corrected
GET /subscriptionsstatus filter enum:expiredreplaced withpast_dueto match the actual API and state machine
2026-03-18 — 47 missing API resource groups documented (#792)
Comprehensive documentation pass covering all previously undocumented API endpoints:
- Auth & Session —
/auth(GET/POST/DELETE),/profile,/preferences,/display-mode - Workspace Settings —
/business-profile,/analytics-settings,/tenant-settings,/channels,/switch-tenant,/tenant-context,/tenants - Monitoring —
/health(public, with liveness/readiness probes),/cache,/usage,/dashboard-stats,/encryption-rotation - Deal Management —
/deal-participants(multi-party CRUD + settlements),/negotiations-dashboard - Website Builder —
/site-blocks(CRUD),/ai-website,/domain-check,/domain-register - Platform —
/activity-stream(SSE),/replays,/collect,/queue(dead-letter + retry) - AI Voice & Chat —
/chat,/tts,/realtime-session,/realtime-function,/realtime-usage - Agent Registry —
/agent-registry(peer discovery + contention detection) - Developer Tools —
/import(CSV wizard),/saved-configs-admin,/sandbox-toggle,/payment-intent - Onboarding & Verification —
/onboarding(new user + new workspace),/verify(public credential verification) - System Admin —
/system-admin(stats, impersonation, grants) - Internal Endpoints —
/csp-report,/stripe-webhook,/email-webhook,/twilio-webhook,/twilio,/apilisted as Internal
2026-03-15 — 22 missing public endpoints documented
Comprehensive documentation additions for previously undocumented public APIs:
- Customer Portal — Full authentication flow (
/customer-auth) and buyer self-service portal (/customer-portal): deals, contracts, signing, payment, and profile management. - Deal Discovery — Agent-facing deal discovery REST API (
/deal-discovery) with filtering and schema access. Complements the existing MCPdiscover_dealstool. - Quotes & Invoices —
/quoteswith generate, retrieve by short code, and list actions. - Deal Notifications —
/deal-notificationsfor sending quote, invoice, and reminder notifications via the customer’s preferred channel. - Pricing Simulation —
/simulate-pricing: bundle simulation, scenario comparison, and negotiation history analytics. - Real-Time Events (SSE) —
/events/streamServer-Sent Events endpoint with reconnect, heartbeat, and per-event HMAC signatures. - Widget A/B Tests —
/ab-testsfull CRUD plus promote and the publicresolveendpoint called on every widget load. - Widget Public Endpoints —
/widget-intelligence(popularity & smart defaults),/widget-contract(generate + sign),/widget-discount(promo code validation),/widget-validate(configuration compatibility check), and/widget-payment-preview(full payment breakdown for headless carts). - FX Rates — Unauthenticated
/fx-rateendpoint for multi-currency price display. - Machine-Readable Schemas —
/schema/state-machineand/schema/deal-termsfor programmatic agent integration. - Availability — Booking slot availability checking via publishable key.
- Federation Discovery — Public
/federation/discoverymanifest endpoint for cross-instance federation handshakes. - Budget Management —
/budgetGET/POST for monthly spend caps and alert thresholds. - USDC Payment Rail —
/usdcbalance, transactions, Coinbase charge creation, and wallet registration. - Fraud Signals —
/fraud-signalslist, demotion history, resolve, and manual evaluation.
2026-03-12 — Documentation completeness update
Comprehensive documentation additions for previously undocumented APIs:
- Intelligence API — All 13 endpoint types now documented with full parameter tables, response schemas, scoring weight explanations, risk factor definitions, and SDK examples. Added:
score,risk,close_forecast,pipeline_forecast,win_probability_curve,score_history,score_accuracy. - Negotiations — Added lifecycle state machine diagram,
suggestaction with AI counter-offer strategies, intelligence endpoint for negotiations, and full parameter/response docs for all actions. - Delegations — Added chain depth rules, spending cap cascade explanation, proposal flow (propose/accept/reject/counter), and
X-Delegation-IDusage pattern. - Subscriptions — Added lifecycle state machine with all 5 states,
renew,retry-payment,change, andusage_summaryendpoints. - Webhook Payloads — Added 10 additional payload schemas:
contract.signed,negotiation.proposed,negotiation.countered,negotiation.accepted,subscription.created,subscription.past_due,subscription.renewed,subscription.cancelled,escrow.created. Enhanced signature verification with timestamp replay prevention. - SDK Quick Start — Added 5 new runnable examples: Intelligence API, Negotiations, Webhook Listener, Product/Deal flow with discount.
- Rate Limiting — Full three-tier documentation: per-key trust-level multipliers (L0–L4), per-IP limits, global circuit breaker, all response headers, and retry pattern.
2026-03-05 — Deprecate query parameter authentication
Passing API keys via the ?api_key= query parameter is now deprecated. Requests using this method will receive an X-Deprecation-Warning response header. Migrate to the Authorization: Bearer header or X-API-Key header.
Tenants can opt in to immediate enforcement by setting enforce_header_auth to 1 in tenant settings, which will reject query parameter authentication with 400 Bad Request.
The Node.js SDK already uses header-based authentication — no SDK changes are required.
© 2026 Salesbooth. OpenAPI Spec