Salesbooth is the commerce layer for AI agents
Expose products, pricing, negotiation rules, contracts, payments, permissions, and trust to websites, humans, and autonomous agents through one deal API.
What is Salesbooth?
Salesbooth lets businesses expose their products, pricing, contracts, approvals, and payment flows to humans, websites, and AI agents through one governed deal platform.
The simple test: Can I let an AI agent sell my products, negotiate terms, create a contract, and take payment without giving it unlimited authority? Salesbooth is built so the answer can be yes.
For SaaS and subscription businesses
Let prospects configure seats, negotiate allowed discounts, sign terms, and start billing.
For marketplaces and services
Coordinate quotes, bookings, deposits, approvals, participants, and fulfillment workflows.
For AI builders
Give ChatGPT, AI assistant, or your own agent controlled commerce tools instead of brittle custom REST glue.
Start with the outcome
Most teams should choose one of these paths before opening the endpoint reference.
1. Embed a sales widget on your website
Use the browser-safe widget when the buyer is on your site and you want a complete guided commerce surface.
- Show product and configuration options.
- Capture the customer and saved configuration.
- Create a quote or deal and collect payment.
2. Give an AI agent permission to sell
Use Agent Commerce when an AI seller or buyer needs to discover offers, negotiate, and complete deals safely.
- Discover products and offers through controlled tools.
- Negotiate inside discount, spend, trust, and approval limits.
- Create deals, send contracts, and request payment.
3. Run deal infrastructure through an API
Use the Core Commerce API when your app owns the interface and Salesbooth is the deal system underneath.
- Manage customers, products, deals, contracts, and payments.
- Listen to webhooks and real-time deal events.
- Keep an audit trail for every commercial action.
Agent commerce in plain English
Connect ChatGPT, Claude, or your own agent to Salesbooth. Your agent can discover offers, negotiate, create deals, check rules, and complete purchases through controlled tools.
Agents cannot spend unlimited money. You control what they can see, negotiate, sign, and spend with API scopes, CORS restrictions, delegation budgets, trust levels, approval thresholds, and audit logs.
MCP turns the platform into tools. Salesbooth exposes 192 MCP tools across 28 categories so agents can work with products, deals, contracts, payments, subscriptions, and approvals without hand-coded REST calls for every action.
Plain-English agent examples
Sell within a cap
Create an agent that can sell subscriptions up to $500/month without approval.
Negotiate, but not sign
Allow a purchasing agent to negotiate commercial terms but require a human before signature.
Discover before payment
Let agents discover deals and build quotes, then require approval before money moves.
End-to-end demo: 20 seats bought by an AI agent
An AI buyer wants 20 seats of software. Salesbooth lets the agent discover the offer, negotiate a discount, request approval when needed, sign the contract, and pay through one governed flow.
The agent calls MCP tools such as discover_deals or list_products to find a subscription product that can be sold to the buyer.
It checks configuration and volume pricing with calculate_pricing or the Core API before proposing terms.
The agent uses negotiate_terms, but discount caps and delegation budgets decide whether the offer can continue automatically.
Salesbooth creates the buyer, then uses create_deal and add_deal_item so the quote becomes a trackable deal.
If the deal exceeds a threshold, the workflow waits for workflow_approve_step or escalates through the approval queue.
The approved deal becomes a contract with create_contract_from_deal, then payment is collected with create_payment_intent or a payment link.
Overview
The Salesbooth API is a RESTful API that uses application/json by default for both requests and responses. Start with the outcome above, then use the smallest spec that matches your integration.
If you are integrating for the first time, start with the smaller audience-specific specs. Admin and internal dashboard APIs are not published publicly.
OpenAPI spec endpoints
These public spec aliases return raw OpenAPI JSON rather than the standard API envelope, making them suitable for code generation, agent tool discovery, and Try-it clients.
curl https://api.salesbooth.com/v1/public-openapi.json404, 405, and 429.curl https://api.salesbooth.com/v1/agent-openapi.json404, 405, and 429.curl https://api.salesbooth.com/v1/core-openapi.json404, 405, and 429.curl https://api.salesbooth.com/v1/widget-openapi.json404, 405, and 429.Build This in 30 Minutes
Build a server-side deal flow: Create customer → create deal → add products → send contract → collect payment.
Build an embedded commerce flow: Embed widget → save config → convert to deal.
Build an agent-first commerce flow: Agent discovers offer → negotiates → signs → pays.
Exceptions are documented on the affected endpoint cards: multipart/form-data uploads, text/event-stream event streams, text/csv and application/x-ndjson deal exports, text/csv audit exports, and text/html invoice downloads.
https://api.salesbooth.com/v1/{
"error": false,
"success": true,
"data": {
"name": "Salesbooth API",
"version": "1.20.2",
"base_url": "https://api.salesbooth.com/v1",
"documentation": "https://salesbooth.com/docs",
"openapi_spec": "https://api.salesbooth.com/openapi.json"
}
}Response Format
{
"error": false,
"success": true,
"data": {}
}{
"error": true,
"code": "validation_error.invalid_fields",
"category": "validation_error",
"message": "Name is required",
"recovery": {
"action": "fix_request",
"retryable": false
},
"ref": "abc123",
"details": {
"field": "name",
"issue": "required"
}
}Authentication
Authenticate secret API requests with an API key sent as a Bearer token or via the X-API-Key header. Secret keys (sb_live_{region}_*, sb_test_{region}_*) are not accepted in the ?api_key= query parameter.
curl https://api.salesbooth.com/v1/deals \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://api.salesbooth.com/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.
Keys include a two-letter region component (au, eu, us) that routes requests to the correct regional data store. The full format is sb_{type}_{region}_{random}, for example sb_live_au_a1b2c3d4....
Publishable keys and widgets: When you create a widget config via POST /api/v1/widget-config, the response includes an api_key (sb_pub_{region}_*). Pass this as the api-key attribute on the <salesbooth-deal> element. Never use a secret key (sb_live_{region}_* or sb_test_{region}_*) in client-side code.
Security Notice: Passing secret API keys (sb_live_{region}_*, sb_test_{region}_*) via the ?api_key= query parameter is not supported. Query parameters appear in server logs, browser history, and referrer headers — use header-based authentication instead.
Note: Publishable keys (sb_pub_{region}_*) used by widget public endpoints and other documented public flows (for example /api/v1/widget-config, /api/v1/widget-intelligence, /api/v1/widget-bookings, /api/v1/ab-tests/resolve, widget analytics beacons, and SSE endpoints) are separate endpoint-specific exceptions. Those flows may accept ?api_key= in their own public/browser-oriented auth paths, independent of the standard secret-key authentication flow.
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-hour limit (default: 1,000 req/hr, 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-hour limit, refilled at keyLimit/3600 tokens per second (≈0.278 tok/s for the 1,000 req/hr default).
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, and each HTTP request counts once against that quota. A tools/batch call is always rate-limited at the outer MCP request. In atomic mode, supported tools execute inside one service-layer transaction after scope and delegation pre-validation. In non-atomic best-effort mode, each tool is executed through the normal internal API path and may also consume authenticated per-key quota or return 429 from the backing endpoint. Implement backoff for both the outer MCP request cadence and any inner tool-level 429 responses in non-atomic mode.
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: Current server responses document code values in their serialized form. Most use dotted lowercase notation such as category.specific_code (for example validation_error.invalid_fields, conflict.stale_version, and billing.trust_ceiling_exceeded). Some legacy historical codes are shown below for reference, but integrations should match the documented serialized code values returned by current responses.
Recommendation: Use the category field (always present and stable) for broad programmatic handling, then match the exact documented code values you care about. The stable category values are: validation_error, authentication_error, authorization_error, not_found, conflict, rate_limited, security_error, billing, transient_error, internal_error.
{
"error": true,
"code": "validation_error.invalid_fields",
"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://api.salesbooth.com/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://api.salesbooth.com/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 for broad handling, then match the exact documented code values your integration needs. 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)
Request Errors (405)
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://api.salesbooth.com/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://api.salesbooth.com/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 six 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://api.salesbooth.com/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 data.product.product_id: prod_xxxxxStep 2 — Create a Customer
curl -X POST https://api.salesbooth.com/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 data.customer.customer_id: cust_xxxxxStep 3 — Create a Deal and Add a Line Item
# Create the deal
curl -X POST https://api.salesbooth.com/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 data.deal.deal_id: deal_xxxxx
# Add a line item
curl -X POST "https://api.salesbooth.com/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",
"name": "Pro Plan",
"quantity": 1,
"unit_price": 99.00
}'
# Transition to in_progress so the customer can pay
curl -X POST "https://api.salesbooth.com/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://api.salesbooth.com/v1/payment-intent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{"deal_id": "deal_xxxxx"}'
# Returns a payment client_secret — pass to the payment form 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>Standard /sdk/v1/... URLs revalidate on each request with ETag via Cache-Control: no-cache, must-revalidate. Fingerprinted version URLs are served as immutable with Cache-Control: public, max-age=31536000, immutable.
npm install @salesbooth/nodeconst { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });Step 2 — Create a Product
async function main() {
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_xxxxx
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});Step 3 — Create a Customer & Deal
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
async function main() {
// 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({
title: 'Pro Plan — Jane Smith',
customer_id: customer.customer_id,
currency: 'USD',
tax_rate: 0.10
});
await sb.deals.addItem(deal.deal_id, {
product_id: 'prod_xxxxx',
name: 'Pro Plan',
quantity: 1,
unit_price: 499.00
});
console.log('Deal created:', deal.deal_id);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});SDK Resources: Common SDK resources include sb.deals, sb.customers, sb.products, sb.contracts, sb.webhooks, sb.payments, sb.subscriptions, sb.widgets, sb.tools, and sb.mcp. Each resource exposes the methods documented in the SDK reference; common CRUD resources provide list(), get(), create(), and update(), while workflow resources such as analytics, negotiations, intelligence, and configuration expose resource-specific methods.
Current shipped browser SDK surface: sb.deals, sb.customers, sb.products, sb.contracts, sb.webhooks, sb.audit, sb.discovery, sb.negotiations, sb.templates, sb.intelligence, sb.search, sb.delegations, sb.configuration, sb.productFamilies, sb.optionGroups, sb.productRules, sb.bundleRules, sb.compatibilityRules, sb.savedConfigs, sb.widgets, sb.analytics, sb.payments, sb.gdpr, sb.auditExport, sb.sandbox, sb.workflows, sb.agentTrust, sb.federation, sb.federationPeers, sb.subscriptions, sb.events, sb.dealParticipants, sb.team, sb.availability, sb.staff, sb.bookings, sb.billing, sb.usdc, sb.agentRegistry, sb.tools, sb.mcp, sb.uploads, sb.schema, sb.system, sb.quotes, sb.abTests, sb.fxRate, sb.trust, sb.dealNotifications, sb.productOptionGroups, sb.pricingSimulation, sb.credits, sb.activity, sb.customerAuth, sb.customerPortal, sb.playground, and sb.federationDiscovery.
Step 4 — Transition the Deal
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
async function main() {
// Use the deal_id returned in Step 3
await sb.deals.transition('deal_xxxxx', 'in_progress');
console.log('Deal ready:', 'deal_xxxxx');
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});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://api.salesbooth.com/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", "api_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('salesbooth:deal-created', (e) => {
console.log('Deal created!', e.detail.dealId);
});
</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' });
async function main() {
// Get AI score for a deal
const score = await sb.intelligence.scoreDeal('deal_abc123');
console.log('Score:', score.score);
console.log('Win probability:', score.win_probability);
console.log('Recommendation:', score.recommendation);
console.log('Explanation:', score.explanation);
Object.entries(score.factors).forEach(([name, factor]) => {
console.log(`${name}: ${factor.points}/${factor.max_points} - ${factor.detail}`);
});
// Get pricing suggestions
const suggestion = await sb.intelligence.getPricingSuggestion('deal_abc123');
console.log(`Suggested price: ${suggestion.suggested_price} (win rate: ${suggestion.win_rate_at_suggested})`);
// Get risk assessment
const risk = await sb.intelligence.assessRisk('deal_abc123');
if (risk.risk_level !== 'low') {
const highRisk = risk.risk_factors.filter(f => f.severity === 'high');
highRisk.forEach(f => console.warn(`Risk: ${f.type} — ${f.action}`));
}
// Get pipeline forecast for next 90 days
const forecast = await sb.intelligence.forecastPipeline({ period: '90' });
console.log(`90-day weighted pipeline: $${forecast.total_weighted_pipeline.toLocaleString()}`);
console.log(`Forecast windows: ${Object.keys(forecast.windows).join(', ')}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});Step 7 — Negotiate Deal Terms (optional)
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
async function main() {
// 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 best = suggestions.suggestions[0];
console.log('AI suggests:', best.recommended_terms, `(confidence: ${best.confidence}%)`);
// Counter with AI-suggested terms
await sb.negotiations.counter('deal_abc123', {
proposed_terms: best.recommended_terms,
message: best.rationale
});
// Accept the other party's latest terms
await sb.negotiations.accept('deal_abc123');
// Get full negotiation history
const history = await sb.negotiations.history('deal_abc123');
console.log(`${history.total_rounds} rounds`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});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' });
async function main() {
// 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');
const sigBuf = Buffer.from(sig);
const expectedBuf = Buffer.from(expected);
if (sigBuf.length !== expectedBuf.length || !crypto.timingSafeEqual(sigBuf, expectedBuf))
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;
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});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' });
async function main() {
// 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
let deal = await sb.deals.create({
title: 'Pro Plan — Jane Smith',
customer_id: customer.customer_id,
currency: 'USD',
tax_rate: 0.10
});
deal = await sb.deals.addItem(deal.deal_id, {
product_id: product.product_id,
name: product.name || 'Pro Plan',
quantity: 5,
unit_price: product.price
});
// Apply discount and transition
deal = await sb.deals.applyDiscount(deal.deal_id, { type: 'percentage', value: 10 });
deal = await sb.deals.transition(deal.deal_id, 'in_progress');
console.log(`Deal ${deal.deal_id} ready — total: $${deal.total}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});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
Install the official Python SDK for typed methods, automatic retries, and idempotency key management.
pip install salesboothimport salesbooth
client = salesbooth.SalesBooth(api_key="sb_test_example_key_do_not_use")
# Create a customer
customer = client.customers.create(
name="Jane Smith",
email="jane@example.com",
company="Acme Corp"
)
print("Customer:", customer["customer_id"])
# Create a deal
deal = client.deals.create(
title="Pro Plan — Jane Smith",
customer_id=customer["customer_id"],
currency="USD",
tax_rate=0.10
)
print("Deal:", deal["deal_id"])
# Add a line item
client.deals.add_item(
deal["deal_id"],
product_id="prod_xxxxx",
name="Pro Plan",
quantity=1,
unit_price=499.00
)
print("Line item added")
# Transition to in_progress
deal = client.deals.transition(deal["deal_id"], "in_progress")
print("Deal transitioned:", deal["status"])import salesbooth
from salesbooth.errors import WebhookVerificationError
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_signing_secret"
@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(as_text=True) # raw string — do NOT parse before verifying
try:
salesbooth.verify_signature(body, sig, WEBHOOK_SECRET, timestamp)
except WebhookVerificationError as e:
abort(400, str(e))
event = request.get_json()
print(f"Event: {event['event']} id={event['id']}")
return "", 200import requests
import hmac
import hashlib
API_KEY = "sb_test_example_key_do_not_use"
BASE_URL = "https://api.salesbooth.com/v1"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
# Create a customer
resp = requests.post(
f"{BASE_URL}/customers",
headers=HEADERS,
json={"name": "Jane Smith", "email": "jane@example.com", "company": "Acme Corp"},
)
resp.raise_for_status()
customer = resp.json()["data"]["customer"]
print("Customer:", customer["customer_id"])
# Create a deal
resp = requests.post(
f"{BASE_URL}/deals",
headers=HEADERS,
json={"title": "Example Deal", "customer_id": customer["customer_id"], "currency": "USD", "tax_rate": 0.10},
)
resp.raise_for_status()
deal = resp.json()["data"]["deal"]
print("Deal:", deal["deal_id"])
# Verify a webhook signature manually with hmac/hashlib
def verify_signature(payload: str, timestamp: str, signature: str, secret: str) -> bool:
signed_content = f"{timestamp}.{payload}"
expected = "v1=" + hmac.new(secret.encode(), signed_content.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)curl Examples
curl "https://api.salesbooth.com/v1/deals?status=in_progress&limit=20" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -X POST https://api.salesbooth.com/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://api.salesbooth.com/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
POST endpoints that create or mutate financial records and document Idempotency-Key support accept 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-processing the operation.
Use idempotency keys for: POST /deals, POST /payment-intent, POST /payments, POST /subscriptions, POST /contracts. Keys must be unique per request, 1–255 characters, and should be regenerated for genuinely new operations.
curl -X POST https://api.salesbooth.com/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", "title": "Order 2026-03-17", "currency": "USD" }'
# Network timeout — resend with the same key; no duplicate is created.
curl -X POST https://api.salesbooth.com/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", "title": "Order 2026-03-17", "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)
Write endpoints and actions that route request bodies through RequestValidator accept only the explicitly documented fields shown in their parameter tables. By default, undocumented or system-controlled fields sent in the request body are stripped from the validated payload and never persisted. Endpoints or actions that use strict validation instead reject unknown fields with 400 validation_error.invalid_fields; the offending entries appear in details.fields with code unknown_field. Other endpoints may apply endpoint-specific validation instead of this centralized contract. This prevents mass-assignment attacks where validator-backed routes might otherwise allow a client to 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— on deal creation, only safe initial values are accepted (draft,in_progress); after creation, status changes require theaction=transitionendpoint.payment_status,contract_status— always controlled by state machine transitionssubtotal,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
Strict validation is used on some write actions where the request schema must match exactly. For example, deal actions such as escrow, record-outcome, set-terms (wrapper format), and partial_accept reject unknown request fields instead of ignoring them.
After creation, deal status changes must use the action=transition action endpoint, which enforces the state machine rules. Direct status writes on PATCH are rejected.
Input Validation
Inputs are always subject to server-side checks, but the exact contract is endpoint-specific. Routes and actions that use RequestValidator validate inputs before database writes and return 400 validation_error.invalid_fields with a details.fields map indicating which fields failed and why. Other endpoints may rely on resource-specific guards or different error responses, so check the endpoint documentation before assuming centralized validation coverage. 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 payment provider — raw card numbers never pass through Salesbooth servers. Salesbooth stores only payment provider PaymentIntent IDs and tokenised payment method references.
Your integration scope is reduced: Because card data flows directly from the buyer’s browser to payment provider’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 payment provider’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 | Legacy compatibility mode for older integrations. Prefer the canonical sub-resource routes below such as /deals/{id}/terms, /deals/{id}/audit, /deals/compare-terms, and /deals/export. The query-action forms remain supported. |
| deal_id_1 | First deal ID for the legacy action=compare-terms alias |
| deal_id_2 | Second deal ID for the legacy action=compare-terms alias |
| 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 (used with action=export) |
| max_value | Filter deals with total value <= this amount (used with action=export) |
| search | Search by deal title or deal ID |
| 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%). Values ≥1 are accepted for compatibility and auto-normalized (e.g. 10 → 0.10); prefer decimal form. |
| 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) |
| deposit_required | number | Required deposit amount from the buyer. When set, payment-intent fallback charges this amount instead of the full total. |
| template_id | string | Deal template ID to create from (pre-populates fields from template) |
Query parameter: ?validate_only=true — validates the request body without creating the deal. Returns {"valid": true, "message": "Validation passed"} on success.
curl -X POST https://api.salesbooth.com/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,
"success": true,
"data": {
"deal": {
"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 |
| 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 accepted for compatibility and auto-normalized (e.g. 10 → 0.10); prefer decimal form. |
| deposit_required | number | Required deposit amount in the deal currency. Must be ≥ 0. |
| deal_type | string | one_time or recurring |
| billing_cycle | string | monthly, quarterly, or annual |
| metadata | object | Arbitrary key-value metadata (max depth 3) |
Note: Deal status cannot be changed via PATCH. Use POST /deals?id={deal_id}&action=transition&status={new_status} instead.
curl -X PATCH "https://api.salesbooth.com/v1/deals?id=deal_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"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. When cancelling, optional reason and notes metadata may be sent either as query parameters or in a JSON request body.| Field | Type | Description |
|---|---|---|
| reason | string | Optional cancellation reason. Allowed values: price, competitor, timing, budget, fit, no_response, champion_left, terms, or other. |
| notes | string | Optional free-text notes recorded with the cancellation. |
curl -X DELETE "https://api.salesbooth.com/v1/deals?id=deal_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"1\"" \
-d '{
"reason": "price",
"notes": "Customer chose a lower-priced annual offer from an incumbent vendor"
}'curl -X DELETE "https://api.salesbooth.com/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: W/\"1\""{
"error": false,
"success": true,
"data": {
"message": "Deal cancelled successfully",
"deal_id": "deal_xxxxx",
"deal": {
"deal_id": "deal_xxxxx",
"version": 2,
"status": "cancelled",
"currency": "USD",
"line_items": [],
"pricing": {
"currency": "USD",
"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:31:00Z"
}
}
}{
"error": false,
"success": true,
"data": {
"message": "Item removed",
"item_id": "item_yyyyy",
"deal": {
"deal_id": "deal_xxxxx",
"version": 2,
"status": "draft",
"currency": "USD",
"line_items": [],
"pricing": {
"currency": "USD",
"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:31:00Z"
}
}
}Deal Actions
| Field | Type | Description |
|---|---|---|
| product_id required | string | Product to add |
| name required | string | Line item display name |
| unit_price required | number | Unit price |
| quantity | integer | Quantity (default 1) |
| description | string | Line item description |
| configuration | object | Selected configuration options |
| price_type | string | Pricing model: once_off, recurring, or metered |
| billing_cycle | string | Billing frequency: monthly, quarterly, or annual. Required when price_type is recurring or metered |
curl -X POST "https://api.salesbooth.com/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",
"name": "Professional Plan",
"unit_price": 99.00,
"quantity": 1,
"price_type": "recurring",
"billing_cycle": "monthly"
}'curl "https://api.salesbooth.com/v1/deals/deal_xxxxx/valid-transitions" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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"
}
}
]
}
}Tip: Each transition's action.endpoint field provides the ready-to-use URL for executing that state change.
in_progress, pending_signature, awaiting_signatures, pending_payment, partially_accepted, closed, cancelled, expired. Not all transitions are valid from every state — see the state machine documentation for allowed paths.| 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://api.salesbooth.com/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://api.salesbooth.com/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 optional | integer | New quantity (minimum: 1) |
| unit_price optional | number | Override unit price for the line item (minimum: 0) |
curl -X POST "https://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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. Provide schema-shaped fields such as payment_terms, delivery, and warranty. The server validates the object and stamps the canonical terms_schema_version. See Deal Terms Schema for the full field reference. |
curl -X POST "https://api.salesbooth.com/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": {
"payment_terms": {
"type": "net_30",
"due_date": "2026-04-30T00:00:00Z"
},
"delivery": {
"method": "digital",
"estimated_days": 5
},
"warranty": {
"duration_days": 365
}
}
}'deals:write or 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, code (promo code for tracking), description (human-readable label) |
| 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://api.salesbooth.com/v1/deals?action=create-configured" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"title": "Acme annual plan",
"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, "code": "first_year"}
],
"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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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. Accepted values: price, competitor, timing, budget, fit, no_response, champion_left, terms, or other |
| 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://api.salesbooth.com/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": "budget", "notes": "Prospect paused rollout after budget review on 2026-03-28"}'curl -X POST "https://api.salesbooth.com/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": "competitor",
"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://api.salesbooth.com/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"
}'GET /api/v1/deals?id={deal_id}&action=audit.GET /api/v1/deals?id={deal_id}&action=verify_audit.curl "https://api.salesbooth.com/v1/deals/deal_xxxxx/verify-audit" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"status": "verified",
"total_entries": 14,
"verified_at": "2026-05-14T04:00:00Z"
}
}deals:read scope when using bearer auth or API keys; session auth is also supported. Legacy alias: GET /api/v1/deals?id={deal_id}&action=escrow.curl "https://api.salesbooth.com/v1/deals/deal_xxxxx/escrow" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"escrow_id": "esc_a1b2c3d4e5f6g7h8",
"deal_id": "deal_xxxxx",
"amount": 4500,
"currency": "AUD",
"status": "held",
"conditions": [
{"type": "delivery", "config": {"tracking_required": true}},
{"type": "approval"}
],
"conditions_summary": {
"total": 2,
"met": 1,
"remaining": 1,
"all_met": false
},
"authorization": {
"last_authorized_at": "2026-05-14T04:00:00Z",
"auth_expires_at": "2026-05-21T04:00:00Z",
"auth_days_remaining": 7,
"auth_status": "ok"
},
"released_at": null,
"refunded_at": null,
"expires_at": "2026-05-21T04:00:00Z",
"created_at": "2026-05-14T04:00:00Z",
"updated_at": "2026-05-14T04:00:00Z"
}
}deals:read scope when using bearer auth or API keys; session auth is also supported. Legacy alias: GET /api/v1/deals?id={deal_id}&action=credential.curl "https://api.salesbooth.com/v1/deals/deal_xxxxx/credential" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"credential_id": "cred_abc123",
"deal_id": "deal_xxxxx",
"status": "active",
"key_id": "key_2026_05",
"credential_hash": "4e5f6a7b8c9d...",
"issued_at": "2026-05-14T04:00:00Z",
"revoked_at": null,
"revocation_reason": null,
"credential": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": ["VerifiableCredential", "DealCompletionCredential"],
"issuer": "https://salesbooth.com",
"issuanceDate": "2026-05-14T04:00:00Z",
"credentialSubject": {
"deal_id": "deal_xxxxx",
"status": "closed"
},
"proof": {
"type": "HmacSha256Signature2024",
"created": "2026-05-14T04:00:00Z",
"verificationMethod": "https://salesbooth.com/keys/key_2026_05",
"proofPurpose": "assertionMethod",
"signatureValue": "9f3c8d7e6b5a4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d"
}
}
}
}GET /api/v1/deals?id={deal_id}&action=terms.curl "https://api.salesbooth.com/v1/deals/deal_xxxxx/terms" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"deal_id": "deal_xxxxx",
"terms_schema_version": "1.0",
"terms": {
"terms_schema_version": "1.0",
"payment_terms": {
"type": "net_30",
"due_date": "2026-04-30T00:00:00Z"
},
"delivery": {
"method": "digital",
"estimated_days": 5
},
"warranty": {
"duration_days": 365
}
}
}
}GET /api/v1/deals?action=compare-terms&deal_id_1={id_1}&deal_id_2={id_2}.| Parameter | Type | Description |
|---|---|---|
| deal_id_1 required | string | First deal ID |
| deal_id_2 required | string | Second deal ID to compare against |
curl "https://api.salesbooth.com/v1/deals/compare-terms?deal_id_1=deal_aaaaa&deal_id_2=deal_bbbbb" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"GET /api/v1/deals?id={deal_id}&action=signing_certificate.curl "https://api.salesbooth.com/v1/deals/deal_xxxxx/signing-certificate" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"certificate_version": "1.0",
"generated_at": "2026-05-14T04:00:00Z",
"legislation": "Electronic Transactions Act 1999 (Cth) and state/territory equivalents",
"deal": {
"deal_id": "deal_xxxxx",
"title": "Enterprise Annual Plan",
"status": "pending_payment",
"currency": "AUD",
"total": "4500.00",
"created_at": "2026-05-10T09:00:00Z"
},
"signers": [
{
"signature_id": "sig_abc",
"signer_type": "user",
"signer_id": "user_zzz",
"signature_method": "click-to-sign",
"signature_algorithm": "ed25519",
"signature_hash": "a3f2b1c9...",
"signed_at": "2026-05-12T11:22:00Z",
"is_agent_signer": false
}
],
"consent_events": [],
"hash_chain_verification": {
"status": "verified",
"total_entries": 14,
"verified_at": "2026-05-14T04:00:00Z"
},
"note": "This certificate is produced by Salesbooth and is not legal advice."
}
}text/csv, application/json, or application/x-ndjson. Accepts all standard list filters (status, created_after, created_before, customer_id, search). Rate-limited to 10 exports per hour. The response streams directly — no JSON envelope wrapper. Legacy alias: GET /api/v1/deals?action=export&format={format}.| Parameter | Type | Description |
|---|---|---|
| format | string | Output format: csv (default), json, or jsonl |
| status | string | Filter by deal status (e.g. closed, draft) |
| created_after | string | ISO 8601 date — only include deals created after this date |
| created_before | string | ISO 8601 date — only include deals created before this date |
| customer_id | string | Filter to deals for a specific customer |
curl "https://api.salesbooth.com/v1/deals/export?format=csv&status=closed" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-o deals.csvcurl "https://api.salesbooth.com/v1/deals/export?format=jsonl&created_after=2026-01-01" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-o deals.jsonlcurl "https://api.salesbooth.com/v1/deals/deal_xxxxx/settlements" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"settlements": [
{
"id": "setl_abc",
"deal_id": "deal_xxxxx",
"participant_id": "part_abc",
"amount": "850.00",
"currency": "USD",
"status": "settled",
"payment_method": "payment_provider",
"payment_provider_transfer_id": "tr_abc123",
"usdc_tx_id": null,
"attempts": 1,
"error_message": null,
"settled_at": "2026-03-10T12:00:00Z",
"created_at": "2026-03-09T10:30:00Z",
"participant": {
"role": "seller",
"scope_description": null,
"revenue_share_percent": 85.00,
"agent_name": null,
"agent_key_id": null,
"is_agent": false
}
},
{
"id": "setl_def",
"deal_id": "deal_xxxxx",
"participant_id": "part_def",
"amount": "150.00",
"currency": "USD",
"status": "settled",
"payment_method": "usdc",
"payment_provider_transfer_id": null,
"usdc_tx_id": "0xabc123",
"attempts": 1,
"error_message": null,
"settled_at": "2026-03-10T12:00:00Z",
"created_at": "2026-03-09T10:30:00Z",
"participant": {
"role": "referrer",
"scope_description": "Referral partner",
"revenue_share_percent": 15.00,
"agent_name": "Referral Agent",
"agent_key_id": "key_agent_xxxxx",
"is_agent": true
}
}
]
}
}| Parameter | Type | Description |
|---|---|---|
| id | string | Deal ID to verify (provide either id or contract_id) |
| contract_id | string | Contract ID to verify |
curl "https://api.salesbooth.com/v1/deals/verify?id=deal_abc123"{
"error": false,
"success": true,
"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, phone, or company |
| 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 |
| customer_id | Deprecated. Alias for id. Use id instead. |
curl -X POST https://api.salesbooth.com/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 |
Query parameter: ?validate_only=true — validates the request body without creating the customer. Returns {"valid": true, "message": "Validation passed"} on success.
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://api.salesbooth.com/v1/customers?id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"5\"" \
-d '{
"phone": "+14155552671",
"company": "Acme Corp (updated)",
"status": "active"
}'curl -X DELETE "https://api.salesbooth.com/v1/customers?id=cust_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: W/\"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, subscription, bundle |
| 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 |
| product_id | Deprecated. Alias for id. Use id instead. |
curl -X POST https://api.salesbooth.com/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 or metered |
| 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 |
Query parameter: ?validate_only=true — validates the request body without creating the product. Returns {"valid": true, "message": "Validation passed"} on success.
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 or metered |
| 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://api.salesbooth.com/v1/products?id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"2\"" \
-d '{
"price": 1199.00,
"status": "active",
"stock_quantity": 50
}'curl -X DELETE "https://api.salesbooth.com/v1/products?id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: W/\"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. |
| contract_id | Deprecated. Alias for id. Use id instead. |
| action | Action to perform (requires id): audit, verify_audit, signing_certificate |
| status | Filter: draft, pending, 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 |
deal_id (which auto-populates customer, value, dates, and currency).curl -X POST https://api.salesbooth.com/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://api.salesbooth.com/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.
Query parameter: ?validate_only=true — validates the request body without creating the contract. Returns {"valid": true, "message": "Validation passed"} on success.
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). |
| customer_id | Reassign the contract to a different customer. |
| contract_number | Custom contract reference number (e.g. CTR-2026-001). |
curl -X PATCH "https://api.salesbooth.com/v1/contracts?id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"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://api.salesbooth.com/v1/contracts?id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "If-Match: W/\"2\""Contract Actions
Payments
Manage the payment lifecycle for deals via payment intents. Create payment intents, confirm payments, issue refunds, and record manual payments.
| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal identifier |
curl https://api.salesbooth.com/v1/payments?deal_id=deal_abc123 \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://api.salesbooth.com/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://api.salesbooth.com/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,
"success": true,
"data": {
"payment_intent_id": "pi_xxxxx",
"client_secret": "pi_xxxxx_secret_xxxxx",
"amount": 1099.00,
"currency": "usd",
"status": "requires_payment_method",
"existing": false
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal to confirm payment for |
| payment_intent_id required | string | The payment intent ID |
curl -X POST https://api.salesbooth.com/v1/payments?action=confirm \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_xxxxx", "payment_intent_id": "pi_xxxxx" }'| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal to refund |
| amount | number | Partial refund amount (omit for full refund) |
| reason | string | Refund reason |
curl -X POST https://api.salesbooth.com/v1/payments?action=refund \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"amount": 200.00,
"reason": "Partial refund for returned item"
}'| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal identifier |
| amount required | number | Payment amount (minimum 0.01) |
| method required | string | Payment method: cash, wire, cheque, bank_transfer, or other |
| reference | string | Reference number or identifier for the payment |
| notes | string | Additional notes about the payment |
curl -X POST https://api.salesbooth.com/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, "method": "bank_transfer", "reference": "TXN-001" }'requires_capture state.| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal the payment belongs to |
| payment_intent_id required | string | The payment intent ID to capture |
| amount_to_capture | number | Amount to capture in currency units. Defaults to the full authorized amount. |
curl -X POST https://api.salesbooth.com/v1/payments?action=capture \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"payment_intent_id": "pi_xxxxx"
}'{
"error": false,
"success": true,
"data": {
"payment_intent_id": "pi_xxxxx",
"captured_amount": 1099.00,
"currency": "usd",
"payment_status": "succeeded",
"deal_status": "closed"
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal to charge payment for |
| customer_id required | string | The customer whose saved payment method to charge |
| payment_method_id | string | Specific saved payment method ID. Defaults to the customer's default payment method. |
curl -X POST https://api.salesbooth.com/v1/payments?action=charge-saved-method \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"customer_id": "cust_xyz789"
}'{
"error": false,
"success": true,
"data": {
"payment_intent_id": "pi_xxxxx",
"amount": 1099.00,
"currency": "usd",
"payment_status": "succeeded",
"deal_status": "closed",
"balance_due": 0.00
}
}| Field | Type | Description |
|---|---|---|
| payment_intent_id required | string | The payment intent ID to poll |
curl -X POST https://api.salesbooth.com/v1/payments?action=poll-intent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "payment_intent_id": "pi_xxxxx" }'{
"error": false,
"success": true,
"data": {
"payment_intent_id": "pi_xxxxx",
"status": "succeeded",
"amount": 1099.00,
"amount_received": 1099.00,
"currency": "usd",
"last_payment_error": null
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | The deal to generate a payment link for |
| success_url | string | URL to redirect the customer to after successful payment |
| cancel_url | string | URL to redirect the customer to if they cancel |
curl -X POST https://api.salesbooth.com/v1/payments?action=generate-link \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123" }'{
"error": false,
"success": true,
"data": {
"payment_link": "https://checkout.salesbooth-payments.example/pay/cs_xxxxx",
"checkout_session_id": "cs_xxxxx",
"amount": 1099.00,
"currency": "usd",
"expires_at": "2026-05-12T01:00:00Z"
}
}| Field | Type | Description |
|---|---|---|
| payment_methods | object | Payment method enable/disable toggles. Accepted keys: accept_card, accept_bank, accept_manual (e.g. {"accept_card": true, "accept_bank": false, "accept_manual": true}) |
| invoice_settings | object | Invoice settings. Accepted keys: invoice_prefix and payment_terms_days |
curl -X POST https://api.salesbooth.com/v1/payments?action=save-settings \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"payment_methods": { "accept_card": true, "accept_bank": false, "accept_manual": true },
"invoice_settings": { "invoice_prefix": "INV-", "payment_terms_days": 30 }
}'{
"error": false,
"success": true,
"data": { "message": "Payment settings saved" }
}curl -X POST https://api.salesbooth.com/v1/payments?action=connect-provider \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'{
"error": false,
"success": true,
"data": {
"url": "https://connect.stripe.com/setup/e/acct_xxxxx/xxxxx",
"account_id": "acct_xxxxx"
}
}curl -X POST https://api.salesbooth.com/v1/payments?action=disconnect-provider \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'{
"error": false,
"success": true,
"data": { "message": "payment account disconnected" }
}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, suspended |
| action | string | analytics for subscription metrics; usage_summary (with id) for current cycle metered usage; usage_history (with id) for historical usage records; meters (with id) to list active meters |
curl "https://api.salesbooth.com/v1/subscriptions?status=active" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/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 | Query parameter — must be create |
| billing_cycle required | string | monthly, quarterly, or annual |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=create" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"billing_cycle": "monthly"
}'curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=pause" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123" }'curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=resume" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123" }'end_of_period: true to cancel at the end of the current billing period.| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | Query parameter — must be cancel |
| end_of_period | boolean | If true, cancels at end of the billing period. Defaults to false (immediate cancellation). |
| reason | string | Optional cancellation reason for record-keeping |
| prorate | boolean | Whether to calculate a proration credit for unused time (default: true) |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=cancel" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "end_of_period": true }'curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=renew" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123" }'past_due subscription. Optionally extends the grace period.| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | Query parameter — must be retry-payment |
| grace_days | integer | Additional days to extend the grace period (optional) |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=retry-payment" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "grace_days": 3 }'| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | Query parameter — must be change |
| line_items required | array | New line items — each item requires product_id (string), name (string), and unit_price (number ≥0); quantity (number ≥1, default 1) is optional |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=change" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"line_items": [
{ "product_id": "prod_enterprise", "name": "Enterprise Plan", "quantity": 1, "unit_price": 299.00 }
]
}'curl "https://api.salesbooth.com/v1/subscriptions?id=deal_abc123&action=usage_summary" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"subscription_deal_id": "deal_abc123",
"billing_cycle": {
"start": "2026-03-01T00:00:00Z",
"end": "2026-04-01T00:00:00Z"
},
"meters": [
{
"meter_id": "meter_xyz789",
"metric_name": "api_calls",
"billing_model": "per_unit",
"total_quantity": 12547,
"included_quantity": 10000,
"billable_quantity": 2547,
"unit_price": 0.001,
"tiers": null,
"charge": 2.55,
"currency": "USD",
"event_count": 312
}
],
"base_amount": 99.00,
"total_usage_charges": 2.55,
"projected_total": 101.55,
"currency": "USD"
}
}| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | Query parameter — must be change-cycle |
| billing_cycle required | string | monthly, quarterly, or annual |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=change-cycle" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_abc123", "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 | Query parameter — must be add-meter |
| metric_name required | string | Metric identifier (e.g. api_calls, seats, storage_gb) |
| billing_model | string | Billing model: per_unit, tiered, or volume (default: per_unit) |
| unit_price | number | Price charged per unit for per_unit billing; defaults to 0 when omitted. tiered and volume meters use tiers. |
| included_quantity | integer | Included free units per billing period (default: 0) |
| tiers | array | Pricing tiers for tiered or volume billing models |
| currency | string | Currency code (e.g. USD); defaults to the subscription currency |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=add-meter" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"metric_name": "api_calls",
"billing_model": "per_unit",
"unit_price": 0.001,
"included_quantity": 10000,
"currency": "USD"
}'| Field | Type | Description |
|---|---|---|
| deal_id | string | Subscription deal ID (not required; meter is identified by meter_id) |
| action required | string | Query parameter — must be remove-meter |
| meter_id required | string | ID of the meter to remove |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=remove-meter" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"meter_id": "meter_xyz789"
}'| Field | Type | Description |
|---|---|---|
| deal_id required | string | Subscription deal ID |
| action required | string | Query parameter — must be record-usage |
| metric_name required | string | Metric 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 |
| metadata | object | Optional key-value metadata attached to this usage event |
| records | array | Batch recording: array of usage objects (each with metric_name, quantity, and optional idempotency_key/metadata) |
curl -X POST "https://api.salesbooth.com/v1/subscriptions?action=record-usage" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"deal_id": "deal_abc123",
"metric_name": "api_calls",
"quantity": 150,
"idempotency_key": "usage-2026-03-12-batch-7",
"metadata": { "source": "api-gateway" }
}'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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/v1/staff?id=stf_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "title": "Lead Consultant" }'curl -X DELETE "https://api.salesbooth.com/v1/staff?id=stf_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://api.salesbooth.com/v1/staff?id=stf_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" }
]
}'is_available: false) or set custom hours for a specific date.| Field | Type | Description |
|---|---|---|
| override_date required | string | Date to override in YYYY-MM-DD format |
| is_available | boolean | Whether the staff member is available on this date (default: false) |
| start_time | string | Availability start time in HH:MM format — required when is_available is true |
| end_time | string | Availability end time in HH:MM format — required when is_available is true |
| reason | string | Optional note describing the reason for the override (e.g. public holiday, conference) |
curl -X POST "https://api.salesbooth.com/v1/staff?id=stf_xxxxx&action=override" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"override_date": "2026-12-25",
"is_available": false,
"reason": "Public holiday"
}'curl -X POST "https://api.salesbooth.com/v1/staff?id=stf_xxxxx&action=override" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"override_date": "2026-12-31",
"is_available": true,
"start_time": "09:00",
"end_time": "13:00",
"reason": "Half day"
}'| Parameter / Field | Type | Description |
|---|---|---|
| override_id required | integer | ID of the override to update (query string or request body) |
| is_available | boolean | Updated availability flag |
| start_time | string | Updated start time in HH:MM format |
| end_time | string | Updated end time in HH:MM format |
| reason | string | Updated reason note |
curl -X PATCH "https://api.salesbooth.com/v1/staff?id=stf_xxxxx&action=override&override_id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"start_time": "10:00",
"end_time": "14:00"
}'| Parameter | Type | Description |
|---|---|---|
| override_id required | integer | ID of the override to remove |
curl -X DELETE "https://api.salesbooth.com/v1/staff?id=stf_xxxxx&action=override&override_id=42" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| product_id required | string | The product to assign the staff member to |
curl -X POST "https://api.salesbooth.com/v1/staff?id=stf_xxxxx&action=assign_product" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "product_id": "prod_xxxxx" }'| Parameter | Type | Description |
|---|---|---|
| id required | string | Staff member ID |
| product_id required | string | The product to remove the assignment for |
curl -X DELETE "https://api.salesbooth.com/v1/staff?id=stf_xxxxx&action=assign_product&product_id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"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://api.salesbooth.com/v1/bookings?status=confirmed \
-H "Authorization: Bearer sb_test_example_key_do_not_use"held status automatically and expires after 10 minutes if not confirmed via PATCH.| 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 (5–480, 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://api.salesbooth.com/v1/bookings \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_xxxxx",
"staff_id": "stf_xxxxx",
"date": "",
"start_time": "10:00",
"customer_name": "Alice Johnson",
"customer_email": "alice@example.com"
}' {
"error": false,
"success": true,
"data": {
"booking": {
"booking_id": "bkng_xxxxx",
"tenant_id": "tenant_xxxxx",
"deal_id": null,
"line_item_id": null,
"product_id": "prod_xxxxx",
"product_name": "60-min Consultation",
"staff_id": "stf_xxxxx",
"staff_name": "Jane Smith",
"customer_id": null,
"customer_name": "Alice Johnson",
"customer_email": "alice@example.com",
"customer_phone": null,
"booking_date": "",
"start_time": "10:00",
"end_time": "11:00",
"status": "held",
"hold_expires_at": "",
"notes": null,
"created_at": "",
"updated_at": ""
}
}
} 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://api.salesbooth.com/v1/bookings?id=bkng_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://api.salesbooth.com/v1/bookings?id=bkng_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": "stf_xxxxx",
"date": "",
"start_time": "14:00"
}' curl -X DELETE "https://api.salesbooth.com/v1/bookings?id=bkng_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://api.salesbooth.com/v1/availability?product_id=prod_xxxxx&date=<future YYYY-MM-DD>" \
-H "Authorization: Bearer sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"slots": [
{ "start_time": "09:00:00", "end_time": "10:00:00", "staff_id": "stf_abc123", "staff_name": "Alice", "duration": 60 },
{ "start_time": "10:30:00", "end_time": "11:30:00", "staff_id": "stf_abc123", "staff_name": "Alice", "duration": 60 },
{ "start_time": "14:00:00", "end_time": "15:00:00", "staff_id": "stf_def456", "staff_name": "Bob", "duration": 60 }
],
"date": "",
"product_id": "prod_xxxxx"
}
} curl "https://api.salesbooth.com/v1/availability?product_id=prod_xxxxx&month=<future YYYY-MM>" \
-H "Authorization: Bearer sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"dates": ["", ""],
"month": "",
"product_id": "prod_xxxxx"
}
} 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. Most write operations require agent:negotiate scope; the accept action requires the elevated agent:execute scope.
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 for propose, counter, reject, and suggest. The accept action requires agent:execute scope — accepting commits to deal terms and is treated as an execution action, not a negotiation action. An agent with only agent:negotiate scope will receive 403 authorization_error.insufficient_scope when calling accept.
Scope summary:
| Action | Required scope |
|---|---|
propose | agent:negotiate |
counter | agent:negotiate |
reject | agent:negotiate |
suggest | agent:negotiate |
accept | agent:execute (elevated — commits deal terms) |
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://api.salesbooth.com/v1/deal-negotiations?deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"deal_id": "deal_abc123",
"total_rounds": 2,
"rounds": [
{
"negotiation_id": "neg_111aaa",
"deal_id": "deal_abc123",
"round_number": 1,
"proposer_type": "agent",
"proposer_id": "agent_xyz",
"proposed_terms": { "discount_percent": 15, "quantity": 10 },
"message": "Volume order — requesting 15% discount",
"status": "proposal",
"expires_at": "2026-03-16T10:00:00Z",
"created_at": "2026-03-09T10:00:00Z"
},
{
"negotiation_id": "neg_222bbb",
"deal_id": "deal_abc123",
"round_number": 2,
"proposer_type": "merchant",
"proposer_id": "user_merchant1",
"proposed_terms": { "discount_percent": 10, "quantity": 10 },
"message": "We can offer 10% for this volume",
"status": "counter",
"expires_at": "2026-03-18T10:00:00Z",
"created_at": "2026-03-09T10:05:00Z"
}
]
}
}{
"error": false,
"success": true,
"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, reject, or suggest. Use the dedicated action=suggest card below for the AI suggestion response details. Note: accept requires agent:execute scope; all other actions require agent:negotiate scope.| Field | Type | Description |
|---|---|---|
| action required | string | propose, counter, accept, reject, suggest. The accept action requires agent:execute scope; others require agent:negotiate. |
| 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 |
| current_terms | object | Current terms to analyse when action=suggest. If omitted, the latest round terms are used automatically. |
| 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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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,
"success": true,
"data": {
"suggestions": [
{
"type": "split_difference",
"recommended_terms": {
"discount_percent": 10,
"payment_terms": "net_15",
"custom_terms": {
"proposed_total": 2250,
"original_total": 2500
}
},
"confidence": 82,
"rationale": "10% discount with net-15 closes 69% of similar deals; net-30 adds payment risk",
"risk_level": "low",
"risk_assessment": "Low concession risk because the counter preserves margin while improving close probability."
},
{
"type": "discount_aggressive",
"recommended_terms": {
"discount_percent": 7,
"payment_terms": "net_7",
"custom_terms": {
"proposed_total": 2325,
"original_total": 2500
}
},
"confidence": 61,
"rationale": "Maximum margin protection; 41% acceptance rate at this price point",
"risk_level": "medium",
"risk_assessment": "Higher rejection risk, but protects margin and shortens payment terms."
}
],
"deal_id": "deal_abc123",
"suggestion_id": "sugg_abc123",
"data_quality": "high",
"historical_deals": 52,
"currency": "USD",
"reference_price": 2500,
"current_proposed_price": 2125
}
}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 |
| is_active | string | Filter: 1 (active, default), 0 (inactive) |
| search | string | Search by name or description |
| limit | integer | Max results, 1–100 (default: 50) |
| offset | integer | Pagination offset |
curl https://api.salesbooth.com/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 |
| template_data required | object | Template content: line_items, discounts, terms, currency, etc. |
| is_active | boolean | Whether the template is active (default: true) |
curl -X POST https://api.salesbooth.com/v1/deal-templates \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise Starter Pack",
"template_data": {
"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 |
| currency | string | Optional top-level currency override |
| tax_rate | string | Optional top-level tax rate override |
| title | string | Optional top-level title override |
| description | string | Optional top-level description override |
| presentation_currency | string | Optional top-level presentation currency override |
| settlement_currency | string | Optional top-level settlement currency override |
| metadata | object | Optional metadata merged into the template metadata for the created deal |
| line_items | array | Optional per-line-item overrides keyed by template line-item index |
| discounts | array | Optional replacement discounts for the instantiated deal |
| terms | object | Optional replacement terms for the instantiated deal |
curl -X POST "https://api.salesbooth.com/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" }'| Field | Type | Description |
|---|---|---|
| name | string | Optional name override for the cloned template |
| description | string | Optional description override for the cloned template |
curl -X POST "https://api.salesbooth.com/v1/deal-templates?id=dtpl_xxxxx&action=clone" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "name": "Copy of Enterprise Starter Pack" }'curl -X PATCH "https://api.salesbooth.com/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://api.salesbooth.com/v1/deal-templates?id=dtpl_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/v1/deal-templates?id=dtpl_xxxxx&action=versions" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"template_id": "dtpl_xxxxx",
"current_version": 3,
"versions": [
{ "version_number": 3, "name": "Enterprise Starter Pack", "description": "Updated pricing tiers", "changed_by": 123, "change_summary": "Updated pricing", "created_at": "2026-05-10T12:00:00Z" },
{ "version_number": 2, "name": "Enterprise Starter Pack", "description": "Added support option", "changed_by": 123, "change_summary": "Added line item", "created_at": "2026-04-22T09:15:00Z" },
{ "version_number": 1, "name": "Enterprise Starter Pack", "description": "Initial version", "changed_by": 123, "change_summary": "Initial version", "created_at": "2026-04-01T08:00:00Z" }
]
}
}| Parameter | Type | Description |
|---|---|---|
| version required | integer | Version number to retrieve |
curl "https://api.salesbooth.com/v1/deal-templates?id=dtpl_xxxxx&action=version&version=2" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| from required | integer | Earlier version number |
| to required | integer | Later version number |
curl "https://api.salesbooth.com/v1/deal-templates?id=dtpl_xxxxx&action=diff&from=1&to=3" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| version required | integer | Version number to roll back to |
curl -X POST "https://api.salesbooth.com/v1/deal-templates?id=dtpl_xxxxx&action=rollback" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "version": 2 }'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://api.salesbooth.com/v1/delegations?id=del_xxxxx&action=verify" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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": "",
"grantee_key_id": "key_agent_xxxxx"
}
} id parameter is required — the delegation is resolved from the API key used for the request.curl "https://api.salesbooth.com/v1/delegations?action=check" \
-H "Authorization: Bearer sb_agent_key_do_not_use"{
"error": false,
"success": true,
"data": {
"valid": true,
"allowed_actions": ["deals:read", "agent:negotiate"],
"spending": {
"max_transaction_amount": 5000.00,
"spent_today": 1200.00,
"remaining_today": 3800.00
},
"expires_at": ""
}
} | Parameter | Type | Description |
|---|---|---|
| id required | string | Delegation ID |
curl "https://api.salesbooth.com/v1/delegations?action=spending&id=del_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"delegation_id": "del_xxxxx",
"spent_today": 3750.00,
"spent_this_month": 48200.00,
"reserved_today": 500.00,
"reserved_this_month": 1200.00,
"max_daily_amount": 25000.00,
"max_monthly_amount": null,
"available_today": 20750.00,
"available_this_month": null,
"max_transaction_amount": 5000.00,
"daily_utilization_pct": 17.0,
"monthly_utilization_pct": null
}
}| Parameter | Type | Description |
|---|---|---|
| id required | string | Delegation ID |
curl "https://api.salesbooth.com/v1/delegations?action=analytics&id=del_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"delegation": {
"delegation_id": "del_xxxxx",
"grantee_agent_key_id": "key_agent_xxxxx",
"allowed_actions": ["deals:read", "deals:write", "agent:negotiate"],
"max_daily_amount": "25000.00",
"spent_today": "3750.00",
"reserved_today": "500.00",
"available_today": "20750.00"
},
"spending": {
"spent_today": "3750.00",
"spent_this_week": "12850.00",
"spent_this_month": "48200.00",
"reserved_today": "500.00",
"lifetime_spend": "62400.00",
"avg_daily_spend": "4457.14",
"daily_utilization_pct": 17,
"monthly_utilization_pct": null
},
"burn_rate": {
"monthly_daily_rate": "1606.67",
"monthly_days_remaining": null,
"projected_exhaustion_date": null,
"days_in_month": 30,
"days_elapsed": 30
},
"deals": {
"count": 14,
"total_value": "62400.00",
"close_rate": 0.8571,
"items": [
{
"deal_id": "deal_xxxxx",
"title": "Annual SaaS plan",
"status": "signed",
"value": "5200.00",
"customer_id": "cus_xxxxx",
"created_at": "2026-04-16 10:30:00"
}
]
},
"roi": {
"deal_value": "62400.00",
"platform_cost": "62400.00",
"roi_ratio": 1
},
"chart": [
{ "day": "2026-04-16", "spend": 4200 }
],
"activity": {
"total": 3,
"offset": 0,
"limit": 100,
"items": [
{
"action": "deal_created",
"entity_type": "deal",
"entity_id": "deal_xxxxx",
"created_at": "2026-04-16 10:30:00",
"outcome": "success"
}
]
}
}
}curl "https://api.salesbooth.com/v1/delegations?action=summary" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"active_delegations": 3,
"total_spent_today": "3750.00",
"total_reserved_today": "500.00",
"approaching_limit": 1,
"recent_actions": [
{
"action": "deal_created",
"entity_id": "del_xxxxx",
"created_at": "2026-04-16 10:30:00"
}
]
}
}delegations:read scope.| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max results (default: 50, max: 100) |
| offset | integer | Pagination offset (default: 0) |
curl "https://api.salesbooth.com/v1/delegations?action=pending" \
-H "Authorization: Bearer sb_agent_key_do_not_use"{
"error": false,
"success": true,
"data": {
"proposals": [
{
"id": "dprop_xxxxx",
"status": "pending",
"proposer_api_key_id": "key_agent_aaaaa",
"target_api_key_id": "key_agent_bbbbb",
"proposer_trust_level": 2,
"proposer_trust_label": "trusted"
}
],
"pagination": { "total": 1, "limit": 50, "offset": 0, "count": 1 }
}
}| Parameter | Type | Description |
|---|---|---|
| id required | string | Delegation proposal ID (dprop_ prefix) |
curl "https://api.salesbooth.com/v1/delegations?action=proposal&id=dprop_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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 },
"round": 1,
"expires_at": ""
}
} | Parameter | Type | Description |
|---|---|---|
| id required | string | Delegation ID |
| from_date | string | Filter from date (ISO 8601) |
| to_date | string | Filter to date (ISO 8601) |
| action_type | string | Filter by action type (e.g. deal_created, deal_signed) |
| entity_type | string | Filter by entity type (e.g. deal, customer) |
| limit | integer | Max results (default: 50) |
| offset | integer | Pagination offset |
curl "https://api.salesbooth.com/v1/delegations?action=activity&id=del_xxxxx&from_date=2026-04-01" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"items": [
{
"action": "deal_created",
"entity_type": "deal",
"entity_id": "deal_xxxxx",
"created_at": "2026-04-12T14:30:00Z",
"outcome": "success",
"details": {}
}
],
"total": 42,
"limit": 50,
"offset": 0
}
}Note: Replace example expires_at values with a real future timestamp before sending a request.
| 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://api.salesbooth.com/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": ""
}' {
"error": false,
"success": true,
"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": "",
"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://api.salesbooth.com/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": "" }' curl -X DELETE "https://api.salesbooth.com/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://api.salesbooth.com/v1/delegations/proposals" \
-H "Authorization: Bearer sb_agent_key_do_not_use"{
"error": false,
"success": true,
"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": "",
"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://api.salesbooth.com/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,
"success": true,
"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": "",
"created_at": "2026-03-16T10:00:00Z"
}
} curl -X POST "https://api.salesbooth.com/v1/delegations/proposals/dprop_xxxxx/accept" \
-H "Authorization: Bearer sb_agent_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/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://api.salesbooth.com/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,
"success": true,
"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": "",
"created_at": "2026-03-16T10:05:00Z"
}
} Using a Delegation
Pass the X-Delegation-ID header to operate within a delegation’s constraints. Draft POST /deals creation validates delegation presence and scope, while configured deal creation computes the estimated total before enforcing max_transaction_amount, daily, and monthly budget caps.
curl -X POST "https://api.salesbooth.com/v1/deals?action=create-configured" \
-H "Authorization: Bearer sb_agent_key_do_not_use" \
-H "X-Delegation-ID: del_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Kitchen renovation quote",
"customer": {
"name": "Jordan Lee",
"email": "jordan@example.com"
},
"items": [
{
"product_id": "prod_kitchen_package",
"name": "Kitchen renovation package",
"quantity": 1,
"unit_price": 6500.00
}
],
"currency": "USD"
}'
# If the computed deal total exceeds 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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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 | string | Optional product scope; supply to limit this rule to a specific product |
| option_ids required | array | Option IDs that trigger the bundle |
| discount_type required | string | percent or fixed |
| discount_value required | number | Discount amount (percent or fixed) |
| min_options | integer | Minimum options needed (default: all) |
curl -X POST https://api.salesbooth.com/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": "percent",
"discount_value": 15,
"min_options": 3
}'curl -X PATCH "https://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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 | string | Optional product scope; supply to limit this rule to a specific product |
| message | string | User-facing message when rule triggers |
curl -X POST https://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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 |
curl "https://api.salesbooth.com/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://api.salesbooth.com/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
}'| Parameter | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
| group_id | string | Option group to update on the default link update path |
| option_id | string | Option to update when action=availability |
| action | string | availability to update per-option availability instead of link overrides |
curl -X PATCH "https://api.salesbooth.com/v1/product-option-groups?product_id=prod_xxxxx&group_id=grp_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"sort_order_override": 2
}'curl -X PATCH "https://api.salesbooth.com/v1/product-option-groups?product_id=prod_xxxxx&option_id=opt_xxxxx&action=availability" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"is_available": false,
"price_modifier_override": 25
}'| Parameter | Type | Description |
|---|---|---|
| product_id required | string | The product identifier |
| group_id | string | Option group to unlink on the default unlink path |
| option_id | string | Option availability override to remove when action=availability |
| action | string | availability to remove a per-option availability override |
curl -X DELETE "https://api.salesbooth.com/v1/product-option-groups?product_id=prod_xxxxx&group_id=grp_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -X DELETE "https://api.salesbooth.com/v1/product-option-groups?product_id=prod_xxxxx&option_id=opt_xxxxx&action=availability" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"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://api.salesbooth.com/v1/product-rules?product_id=prod_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| product_id required | string | Query parameter for the product identifier |
| action required | string | Query parameter that must be validate |
| option_ids required | array | Array of selected option IDs to validate |
curl -X POST "https://api.salesbooth.com/v1/product-rules?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_ssd_512", "opt_ram_32gb"]
}'{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/configuration?product_id=prod_xxxxx \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://api.salesbooth.com/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://api.salesbooth.com/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,
"success": true,
"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://api.salesbooth.com/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,
"success": true,
"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.
?id= to retrieve a single webhook by ID.| Field | Type | Description |
|---|---|---|
| id | string | If provided, returns a single webhook object instead of a list |
| status | string | Filter by status: active, paused, or failed |
| limit | integer | Number of results per page (offset-based pagination) |
| offset | integer | Number of results to skip (offset-based pagination) |
| after | string | Return results after this cursor (cursor-based pagination) |
| before | string | Return results before this cursor (cursor-based pagination) |
| sort | string | Sort order for results (e.g. created_at:desc) |
| 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://api.salesbooth.com/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"]
}'{
"error": false,
"success": true,
"data": {
"webhook": {
"webhook_id": "wh_abc123",
"url": "https://myapp.com/webhooks/salesbooth",
"events": ["deal.created"],
"description": null,
"status": "active",
"secret": "whsec_abc123...",
"created_at": "2026-05-23T10:00:00Z"
}
}
}secret field is only returned once — on creation. Store it securely immediately; it cannot be retrieved again. Use it to verify the X-Salesbooth-Signature header on incoming webhook requests. To rotate the secret, call POST /api/v1/webhooks/rotate_secret?id={webhook_id}.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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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": "whd_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://api.salesbooth.com/v1/webhooks/events?since_sequence=1500&limit=50" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"events": [
{
"event_id": "evt_abc123",
"event_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://api.salesbooth.com/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": "whd_abc123",
"webhook_id": "wh_xxxxx",
"event_id": "evt_abc123",
"event_type": "deal.created",
"payload": { "deal_id": "deal_xxx" },
"status": "failed",
"attempts": 3,
"max_attempts": 3,
"response_code": null,
"error_message": "Connection refused",
"is_replay": false,
"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://api.salesbooth.com/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://api.salesbooth.com/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": "whd_new456"
}
]
}
}pending and attempts immediate re-delivery.| Field | Description |
|---|---|
| delivery_id required | ID of the failed delivery to retry |
curl -X POST https://api.salesbooth.com/v1/webhooks/retry \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"delivery_id": "whd_abc123"
}'{
"error": false,
"success": true,
"data": {
"delivery": {
"delivery_id": "whd_abc123",
"status": "success",
"attempts": 1,
"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://api.salesbooth.com/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 X-Salesbooth-Signature, X-Salesbooth-Timestamp, and X-Salesbooth-Delivery-Id headers.
{
"event": "deal.created",
"timestamp": "2026-03-09T10:30:00Z",
"tenant_id": "tenant_abc123",
"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"
}
}{
"event": "deal.status_changed",
"timestamp": "2026-03-09T11:00:00Z",
"tenant_id": "tenant_abc123",
"data": {
"id": "deal_abc123",
"previous_status": "draft",
"new_status": "in_progress",
"changed_at": "2026-03-09T11:00:00Z"
}
}{
"event": "deal.payment_received",
"timestamp": "2026-03-09T12:00:00Z",
"tenant_id": "tenant_abc123",
"data": {
"deal_id": "deal_abc123",
"amount": "1099.00",
"currency": "USD",
"payment_intent_id": "pi_xxxxx",
"status": "succeeded"
}
}{
"event": "customer.created",
"timestamp": "2026-03-09T10:00:00Z",
"tenant_id": "tenant_abc123",
"data": {
"id": "cust_xxxxx",
"company": "Acme Corp",
"status": "active",
"created_at": "2026-03-09T10:00:00Z"
}
}{
"event": "contract.signed",
"timestamp": "2026-03-09T13:00:00Z",
"tenant_id": "tenant_abc123",
"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"
}
}{
"event": "negotiation.proposed",
"timestamp": "2026-03-09T14:00:00Z",
"tenant_id": "tenant_abc123",
"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"
}
}{
"event": "negotiation.countered",
"timestamp": "2026-03-09T15:00:00Z",
"tenant_id": "tenant_abc123",
"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"
}
}{
"event": "negotiation.accepted",
"timestamp": "2026-03-09T16:00:00Z",
"tenant_id": "tenant_abc123",
"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"
}
}{
"event": "subscription.created",
"timestamp": "2026-03-09T17:00:00Z",
"tenant_id": "tenant_abc123",
"data": {
"deal_id": "deal_abc123",
"billing_cycle": "monthly",
"status": "active",
"next_renewal_at": "2026-04-09T17:00:00Z",
"amount": "99.00",
"currency": "USD"
}
}{
"event": "subscription.past_due",
"timestamp": "2026-04-09T17:05:00Z",
"tenant_id": "tenant_abc123",
"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
}
}{
"event": "escrow.created",
"timestamp": "2026-03-10T10:00:00Z",
"tenant_id": "tenant_abc123",
"data": {
"id": "escrow_xxxxx",
"deal_id": "deal_abc123",
"amount": "2500.00",
"currency": "USD",
"release_condition": "delivery_confirmed",
"expires_at": "2026-06-10T10:00:00Z"
}
}{
"event": "subscription.renewed",
"timestamp": "2026-04-09T17:00:00Z",
"tenant_id": "tenant_abc123",
"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"
}
}{
"event": "subscription.cancelled",
"timestamp": "2026-03-12T10:00:00Z",
"tenant_id": "tenant_abc123",
"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 an X-Salesbooth-Signature header (v1= + HMAC-SHA256 of timestamp + "." + raw body) and an X-Salesbooth-Timestamp header (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)
const signatureBuf = Buffer.from(signature);
const expectedBuf = Buffer.from(expected);
if (signatureBuf.length !== expectedBuf.length || !crypto.timingSafeEqual(signatureBuf, expectedBuf)) {
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 text/csv 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://api.salesbooth.com/v1/audit/export?entity_type=deal&entity_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/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,
"success": true,
"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 five configurable factors: time_in_status, deal_value, line_items, discount, and customer_history. Default weights are 20 points each, all five factors are required, and weights must be non-negative integers that sum to exactly 100. You can override them per tenant via PATCH ?type=config.
| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | The deal to score |
curl "https://api.salesbooth.com/v1/intelligence?type=score&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"deal_id": "deal_abc123",
"score": 74,
"factors": {
"time_in_status": {
"score": 18,
"max": 20,
"detail": "Deal has been in status for 48 hours, within the configured 72-hour target",
"points": 18,
"max_points": 20
},
"deal_value": {
"score": 16,
"max": 20,
"detail": "Deal value is close to the configured ideal value of $5,000.00",
"points": 16,
"max_points": 20
},
"line_items": {
"score": 14,
"max": 20,
"detail": "Deal includes 3 line items, which is slightly above the tenant average",
"points": 14,
"max_points": 20
},
"discount": {
"score": 12,
"max": 20,
"detail": "Discounting is within the normal range for similar won deals",
"points": 12,
"max_points": 20
},
"customer_history": {
"score": 14,
"max": 20,
"detail": "Customer has a positive close history with this tenant",
"points": 14,
"max_points": 20
}
},
"win_probability": 0.68,
"recommendation": "Follow up while the deal is still within the ideal status window and avoid adding extra discounting unless the buyer pushes on price.",
"explanation": "Strong overall signal driven by healthy time in status, solid customer history, and a deal value close to your target."
}
}{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/intelligence?type=risk&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"deal_id": "deal_abc123",
"risk_level": "medium",
"risk_score": 42,
"risk_factors": [
{
"type": "stall_risk",
"severity": "high",
"severity_score": 20,
"detail": "Deal has been open 18.0 days (2.6x the average of 7.0 days for closed deals)",
"action": "Follow up immediately — deal has been in \"in_progress\" for 18 days, which is 2.6x longer than average"
},
{
"type": "price_risk",
"severity": "low",
"severity_score": 10,
"detail": "Deal value ($12,000.00) is 18% above the average closed deal ($10,200.00) — 1.7 standard deviations",
"action": "Consider adjusting pricing or offering a discount — deal value is significantly above typical"
}
],
"recommended_actions": [
"Follow up immediately — deal has been in \"in_progress\" for 18 days, which is 2.6x longer than average",
"Consider adjusting pricing or offering a discount — deal value is significantly above typical"
]
}
}{
"error": false,
"success": true,
"data": {
"deal_id": "deal_abc123",
"optimistic": "2026-03-28",
"expected": "2026-04-08",
"pessimistic": "2026-04-22",
"confidence": 0.68,
"factors": [
{
"stage": "in_progress",
"avg_days": 14.2,
"sample_size": 184
},
{
"factor": "deal_characteristics",
"adjustment": 1.12,
"detail": "Deal characteristics suggest slower than average close"
}
]
}
}| Parameter | Type | Description |
|---|---|---|
| period | string | 30, 60, 90, or all (default: all) |
{
"error": false,
"success": true,
"data": {
"tenant_id": "tenant_abc123",
"windows": {
"30": { "days": 30, "weighted_value": 48250.00, "deal_count": 9, "at_risk_value": 12000.00 },
"60": { "days": 60, "weighted_value": 96400.00, "deal_count": 17, "at_risk_value": 22600.00 },
"90": { "days": 90, "weighted_value": 142750.00, "deal_count": 23, "at_risk_value": 31850.00 }
},
"total_weighted_pipeline": 142750.00,
"total_at_risk_value": 31850.00,
"active_deals": 23,
"closed_deals_analyzed": 48,
"deals": [
{
"deal_id": "deal_abc123",
"title": "Enterprise renewal",
"status": "in_progress",
"value": 45000.00,
"weighted_value": 27000.00,
"at_risk": true,
"expected_close_window": "60"
}
],
"trend": {
"current_period_weighted": 142750.00,
"previous_period_weighted": 131400.00,
"pct_change": 8.64
},
"generated_at": "2026-03-12 10:00:00"
}
}{
"error": false,
"success": true,
"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,
"success": true,
"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,
"success": true,
"data": {
"overall_accuracy": 79.0,
"total_deals_analyzed": 356,
"calibration": [
{
"score_range": "61-80",
"total_deals": 89,
"won": 61,
"lost": 28,
"win_rate": 68.5,
"avg_value": 12480.55
}
],
"interpretation": "Deal scores predict outcomes with 79% accuracy — scoring model is well-calibrated.",
"weight_suggestions": {
"current_accuracy": 79.0,
"suggestions": [],
"summary": "Scoring model is well-calibrated at 79% accuracy. No weight adjustments needed."
}
}
}curl "https://api.salesbooth.com/v1/intelligence?type=pricing" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"intelligence:write scope. Weights must include all five factors as non-negative integers that sum to 100.| Field | Type | Description |
|---|---|---|
| weights | object | Required keys: time_in_status, deal_value, line_items, discount, and customer_history. Values must be non-negative integers that sum to 100. |
| thresholds | object | Optional factor-specific thresholds. Supported fields: time_in_status.ideal_hours and deal_value.ideal_value. |
curl -X PATCH "https://api.salesbooth.com/v1/intelligence?type=config" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"weights": {
"time_in_status": 30,
"deal_value": 25,
"line_items": 15,
"discount": 10,
"customer_history": 20
},
"thresholds": {
"time_in_status": { "ideal_hours": 72 },
"deal_value": { "ideal_value": 5000 }
}
}'| Field | Type | Description |
|---|---|---|
| weights required | object | Proposed weight configuration to test. Include all five factor keys and make the integer values sum to 100. |
| thresholds | object | Optional threshold overrides to test alongside the proposed weights. |
curl -X POST "https://api.salesbooth.com/v1/intelligence?type=backtest" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"weights": {
"time_in_status": 30,
"deal_value": 25,
"line_items": 15,
"discount": 10,
"customer_history": 20
},
"thresholds": {
"time_in_status": { "ideal_hours": 72 },
"deal_value": { "ideal_value": 5000 }
}
}'{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/intelligence?type=pipeline_deals&stage=in_progress" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/intelligence?type=pricing_suggestions_bulk" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/intelligence?type=suggestion_analytics&days=90" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/intelligence?type=counter_terms&deal_id=deal_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -G "https://api.salesbooth.com/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={"payment_terms":"net60","custom_terms":{"proposed_total":2125,"original_total":2500}}'{
"error": false,
"success": true,
"data": {
"suggestions": [
{
"type": "split_difference",
"recommended_terms": {
"payment_terms": "net30",
"custom_terms": {
"proposed_total": 2312.5,
"original_total": 2500
}
},
"confidence": 82,
"win_probability_delta": 0.14,
"rationale": "Countering at the midpoint preserves margin while improving close probability versus the buyer's net60 ask.",
"risk_level": "low",
"risk_assessment": "Low concession risk because the counter moves toward the buyer while keeping terms inside historical close ranges.",
"acceptance_rate": 0.673
},
{
"type": "counter_offer",
"recommended_terms": {
"payment_terms": "net30",
"custom_terms": {
"proposed_total": 2375,
"original_total": 2500
}
},
"confidence": 61,
"win_probability_delta": 0.08,
"rationale": "A lighter concession protects revenue and still aligns with accepted outcomes from similar deals.",
"risk_level": "medium",
"risk_assessment": "Moderate rejection risk if the buyer is anchored to a deeper discount or longer payment window.",
"acceptance_rate": 0.412
}
],
"deal_id": "deal_abc123",
"suggestion_id": "sugg_abc123",
"data_quality": "high",
"historical_deals": 52,
"currency": "USD",
"reference_price": 2500,
"current_proposed_price": 2125
}
}SDK Example — Score a Deal
const { SalesBooth } = require('@salesbooth/node');
const sb = new SalesBooth({ apiKey: 'sb_test_example_key_do_not_use' });
async function main() {
// Score a deal and inspect the returned factors
const result = await sb.intelligence.scoreDeal('deal_abc123');
console.log('Score:', result.score); // 74
console.log('Win probability:', result.win_probability); // 0.68
console.log('Recommendation:', result.recommendation);
console.log('Explanation:', result.explanation);
Object.entries(result.factors).forEach(([name, factor]) => {
console.log(`${name}: ${factor.points}/${factor.max_points} - ${factor.detail}`);
});
// Get pricing suggestion
const suggestion = await sb.intelligence.getPricingSuggestion('deal_abc123');
console.log('Suggested price:', suggestion.suggested_price);
// Get risk assessment
const risk = await sb.intelligence.assessRisk('deal_abc123');
if (risk.risk_level === 'high') {
console.warn('High-risk deal:', risk.risk_factors.map(f => f.type));
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});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://api.salesbooth.com/v1/activity?event_type=deal.created&limit=20" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/v1/activity?action=notifications" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/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.*) |
| channels | array | Delivery channels array. Allowed values: in_app, email, sms, and webhook. Defaults to ["in_app"] when omitted. If channels includes webhook, provide webhook_url. |
| name | string | Rule display name |
| webhook_url | string | Webhook destination URL. Required when channels includes webhook. |
curl -X POST "https://api.salesbooth.com/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",
"channels": ["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://api.salesbooth.com/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"] }'No request body fields are required. Omit the body or send an empty JSON object.
curl -X POST "https://api.salesbooth.com/v1/activity?action=test_notification" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'curl -X PATCH "https://api.salesbooth.com/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 '{ "channels": ["webhook"], "webhook_url": "https://example.com/activity-webhook" }'curl -X DELETE "https://api.salesbooth.com/v1/activity?action=delete_rule&rule_id=rule_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/v1/activity?action=preferences" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"categories": {
"deals": { "in_app": true, "email_enabled": true, "email_digest": "immediate", "enabled": true }
},
"thresholds": {},
"schedule": {}
}
}curl -X PUT "https://api.salesbooth.com/v1/activity?action=preferences" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"categories": {
"deals": { "in_app": true, "email_enabled": true, "email_digest": "immediate", "enabled": true }
},
"thresholds": {},
"schedule": {}
}'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 operations in a batch run inside one transaction. Any failure rolls back all mutations and returns a structured error with the failed operation and the number of operations completed before rollback. The results array is returned only when every operation succeeds.
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://api.salesbooth.com/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", "email": "buyer@example.com" } },
{ "method": "POST", "resource": "deals", "body": { "title": "Enterprise Plan" } },
{ "method": "PATCH", "resource": "deals", "id": "deal_existing", "body": { "description": "Expanded rollout scope" }, "version": 3 },
{ "method": "PATCH", "resource": "deals", "id": "deal_existing", "action": "transition", "body": { "status": "in_progress" }, "version": 3 }
]
}'Note: Deal status changes in batch requests must use PATCH with a top-level action of transition and the target status inside body.
{
"error": false,
"success": true,
"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" } },
{ "index": 3, "status": 200, "data": { "id": "deal_existing", "status": "in_progress" } }
],
"total": 4
}
}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.
Dashboard-managed builder. AI Sales Agent creation, approval, lifecycle, and knowledge-management actions are part of the Salesbooth admin experience and are not published as public external API endpoints.
Public integration path. Use the agent registry, tools, MCP, deal, contract, payment, widget, and webhook APIs to connect your own agents to Salesbooth's governed commerce workflows.
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: deal_management, product_catalog, customer_management, contracts, negotiation, templates, intelligence, payments, subscriptions, widgets, configuration, saved_configs, webhooks, delegations, compliance, search, audit, sandbox, workflows, agent_trust, federation, agent_registry, deal_participants, closure, catalog_management, documents, billing, headless_flow |
curl https://api.salesbooth.com/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, tools/batch, resources/list, resources/templates/list, resources/read, resources/subscribe, resources/unsubscribe, notifications/list, prompts/list, prompts/get, and ping.curl -X POST https://api.salesbooth.com/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://api.salesbooth.com/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/sdk/agent');
async function main() {
const agent = SalesboothAgent.init({
apiKey: 'sb_test_example_key_do_not_use',
delegationId: 'del_xxxxx' // optional
});
// Get tool definitions for AI provider
const tools = agent.getToolDefinitions('openai');
// Execute a tool
const deals = await agent.executeTool('discover_deals', {
category: 'software',
max_price: 10000
});
}
main().catch(console.error);const SalesboothAgent = require('@salesbooth/sdk/agent');
const agent = SalesboothAgent.init({
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://api.salesbooth.com/v1/tools \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "X-Delegation-ID: del_xxxxx"Agent Workflows
Autonomous deal orchestration. Agents can plan and execute multi-step deal workflows within delegation spending limits.
| Parameter | Type | Description |
|---|---|---|
| action | string | Query action: spending_summary, anomalies, agent_stats, pending_approvals, approval_status |
| id | string | Retrieve a specific workflow |
| status | string | Filter: planned, executing, negotiating, awaiting_approval, completed, failed, cancelled, degraded, dead_letter |
| delegation_id | string | Filter by delegation |
| limit | integer | Max results (default: 50) |
curl https://api.salesbooth.com/v1/agent-workflow?status=executing \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| period | string | Aggregation window: day, week, month (default: month) |
curl "https://api.salesbooth.com/v1/agent-workflow?action=spending_summary&period=week" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"action=detect_anomalies for SDK compatibility.curl "https://api.salesbooth.com/v1/agent-workflow?action=anomalies" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| agent_id required | string | Agent key ID to retrieve statistics for |
| granularity | string | Time grouping: daily, weekly (default: daily) |
curl "https://api.salesbooth.com/v1/agent-workflow?action=agent_stats&agent_id=key_xxxxx&granularity=weekly" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| delegation_id | string | Filter by delegation |
| limit | integer | Max results (default: 50, max: 100) |
| offset | integer | Pagination offset (default: 0) |
curl "https://api.salesbooth.com/v1/agent-workflow?action=pending_approvals" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| id required | string | Workflow ID to check |
curl "https://api.salesbooth.com/v1/agent-workflow?action=approval_status&id=wf_xxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| delegation_id required | string | Delegation providing spending limits |
| intent required | string | Workflow intent enum: purchase, sell, or fulfill |
| constraints | object | Additional planning constraints such as max_budget and product_categories |
curl -X POST https://api.salesbooth.com/v1/agent-workflow \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"delegation_id": "del_xxxxx",
"intent": "purchase",
"constraints": { "max_budget": 5000, "product_categories": ["software"] }
}'curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=execute" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| (modifications) required | object | Key-value fields to update on the workflow’s deal (e.g. price, quantity) |
curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=modify" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "price": 4500, "quantity": 2 }'| Field / Parameter | Type | Description |
|---|---|---|
| token | string | One-time approval token (query param or body field) for token-based approval without a session |
| approved_by | string | Identifier of the approving party (body) |
| note | string | Optional approval note (body) |
curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=approve&token=apt_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "approved_by": "approver@example.com", "note": "Approved from email link" }'curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=approve" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "note": "Approved after budget review" }'| Field / Parameter | Type | Description |
|---|---|---|
| token | string | One-time approval token (query param or body field) for token-based rejection without a session |
| rejected_by | string | Identifier of the rejecting party (body) |
| reason | string | Optional rejection reason (body) |
curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=reject&token=apt_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "rejected_by": "approver@example.com", "reason": "Budget needs manual review" }'curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=reject" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "reason": "Deal value exceeds quarterly budget" }'curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=resend_approval" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| escalation_reason required | string | Reason for escalation (max 2000 characters) |
curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=escalate_approval" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "escalation_reason": "Reviewer has not responded within 12 hours" }'| Field | Type | Description |
|---|---|---|
| delegation_id required | string | Delegation ID granting approval authority |
| approval_note | string | Optional note recorded with the approval (max 2000 characters) |
curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=delegate_approve" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "delegation_id": "del_xxxxx", "approval_note": "Within authorised spend limit" }'curl -X POST "https://api.salesbooth.com/v1/agent-workflow?id=wf_xxxxx&action=cancel" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Agent Trust
Agents cannot spend unlimited money or sign anything they want. Agent Trust gives each agent a controlled envelope for what it can see, negotiate, sign, and spend.
Per-API-key trust scoring, capability gating, and abuse protection determine transaction caps, allowed actions, approval thresholds, 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://api.salesbooth.com/v1/agent-trust \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"key_id": "key_abc123",
"trust_level": 2,
"trust_label": "Established",
"trust_score": 62,
"transaction_cap": 5000,
"capabilities": {
"discover": { "unlocked": true, "required_level": 0, "required_label": "Untrusted" },
"negotiate": { "unlocked": true, "required_level": 1, "required_label": "Provisional" },
"execute": { "unlocked": true, "required_level": 2, "required_label": "Established" },
"autonomous_workflow": { "unlocked": true, "required_level": 2, "required_label": "Established" },
"high_value_deals": { "unlocked": false, "required_level": 3, "required_label": "Trusted" },
"bulk_operations": { "unlocked": false, "required_level": 3, "required_label": "Trusted" },
"priority_support": { "unlocked": false, "required_level": 4, "required_label": "Verified Partner" }
},
"progress": {
"next_level": 3,
"next_label": "Trusted",
"score_required": 150,
"score_current": 62,
"score_percent": 41.3,
"deals_required": 50,
"days_required": 90,
"max_failures_allowed": 5
}
}
}curl "https://api.salesbooth.com/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://api.salesbooth.com/v1/agent-trust-credentials?action=issue" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'{
"error": false,
"success": true,
"data": {
"credential_id": "cred_xyz789",
"status": "active",
"valid_from": "2026-03-12T10:00:00Z",
"valid_until": "2026-06-10T10:00:00Z",
"revoked_at": null,
"revocation_reason": null,
"credential": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"id": "did:web:api.salesbooth.com/credentials/cred_xyz789",
"type": [
"VerifiableCredential",
"AgentTrustCredential"
],
"issuer": "did:web:api.salesbooth.com",
"issuanceDate": "2026-03-12T10:00:00Z",
"validFrom": "2026-03-12T10:00:00Z",
"validUntil": "2026-06-10T10:00:00Z",
"credentialSubject": {
"id": "did:key:agent:key_abc123",
"trustLevel": 2,
"trustScore": 740,
"dealsCompleted": 18,
"disputeRate": 0,
"businessVerified": true,
"issuedAt": "2026-03-12T10:00:00Z"
},
"proof": {
"type": "Ed25519Signature2020",
"created": "2026-03-12T10:00:00Z",
"verificationMethod": "did:web:api.salesbooth.com#tenant_key_123",
"proofPurpose": "assertionMethod",
"proofValue": "base64-encoded-signature"
}
}
}
}curl https://api.salesbooth.com/v1/agent-trust-credentials \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Agent registry
Use Agent Registry for same-tenant multi-agent coordination. It exposes summary-level discovery so agents can see active delegations, inspect high-level activity, and detect overlapping product work without leaking full workflow internals.
agent:discover with bearer auth or API key auth; tenant session auth is also supported. Rate limits are trust-tiered.| Parameter | Type | Description |
|---|---|---|
| action required | string | list_agents, agent_activity, or check_contention |
| status | string | For list_agents: active, expired, revoked, or all |
| capability_scope | string | Filter listed delegations to those with a specific allowed action |
| created_after | date-time | Return delegations created after this ISO 8601 timestamp |
| limit | integer | Pagination limit for list_agents (default: 50, max: 100) |
| offset | integer | Pagination offset for list_agents |
| delegation_id | string | Target delegation for agent_activity |
| agent_identifier | string | Alternative target identifier for agent_activity |
| product_ids | string | Comma-separated product IDs for check_contention |
curl "https://api.salesbooth.com/v1/agent-registry?action=list_agents&status=active&limit=20" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"list_agents returns data.agents[] plus data.pagination; agent_activity returns per-agent workflow and spending summaries; check_contention returns data.contention[] and data.total_contested. Common errors: 400, 401, 403, 404, 429, and 500.Public credential verification
Third parties can verify Salesbooth verifiable credentials without a seller API key. This is the public verification endpoint for compliance, financing, partner federation, and dispute review workflows.
| Field | Type | Description |
|---|---|---|
| credential required | object | W3C Verifiable Credential object to verify. Must include @context, type, and proof. |
curl -X POST https://api.salesbooth.com/v1/verify \
-H "Content-Type: application/json" \
-d '{
"credential": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "AgentTrustCredential"],
"proof": {
"type": "Ed25519Signature2020",
"proofValue": "base64-encoded-signature"
}
}
}'data.valid, data.status, and data.verified_at, with additional issuer, credential, and revocation metadata when available. Common errors: 400, 405, 429, and 500.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
Connect ChatGPT, Claude, or your own agent to Salesbooth. Your agent can discover offers, negotiate, create deals, check rules, and complete purchases through controlled tools.
Salesbooth turns MCP into controlled agent commerce tools across 192 tools in 28 categories, so your agent can work with products, deals, contracts, payments, approvals, resources, and prompts without hand-coded REST calls for every action.
Salesbooth implements the Model Context Protocol (MCP) over HTTP using JSON-RPC 2.0. The MCP endpoint supports resource browsing, prompt templates, and subscriptions so agents can work with the full Salesbooth platform.
Endpoint: POST https://api.salesbooth.com/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, tools/batch, resources/list, resources/templates/list, resources/read, resources/subscribe, resources/unsubscribe, notifications/list, 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://api.salesbooth.com/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, "batch": true },
"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 192 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://api.salesbooth.com/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": ["fixed", "tiered", "volume", "usage"]
},
"payment_terms": { "type": "string", "description": "Filter by payment terms (e.g. \"net_30\", \"net_60\")" },
"limit": { "type": "integer", "minimum": 1, "maximum": 100 },
"offset": { "type": "integer", "minimum": 0 }
},
"required": [],
"additionalProperties": false
}
}
]
}
}Response excerpt; additional tools are omitted.
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": {}
}
}{
"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": "list_customers",
"arguments": { "search": "Acme Corp", "limit": 5 }
}
}{
"jsonrpc": "2.0", "id": "w1b", "method": "tools/call",
"params": {
"name": "create_deal",
"arguments": {
"customer_id": "cust_abc123",
"title": "Enterprise software package",
"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",
"name": "Platform Pro (Enterprise)",
"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_suggestion",
"arguments": { "deal_id": "deal_xyz789" }
}
}{
"jsonrpc": "2.0", "id": "w2b",
"result": {
"content": [{
"type": "text",
"text": "{\"suggested_price\":9500,\"confidence\":0.82,\"factors\":{\"volume_threshold\":\"reached\",\"market_position\":\"competitive\"}}"
}],
"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",
"proposed_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": "suggest_counter_terms",
"arguments": {
"deal_id": "deal_xyz789",
"current_terms": { "discount_percent": 7, "payment_terms": "net_15" }
}
}
}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",
"arguments": { "id": "del_xxxxx" }
}
}{
"jsonrpc": "2.0", "id": "w4a",
"result": {
"content": [{
"type": "text",
"text": "{\"id\":\"del_xxxxx\",\"allowed_actions\":[\"deals:write\",\"agent:negotiate\"],\"spending_limits\":{\"max_transaction_amount\":5000.00,\"max_daily_amount\":10000.00},\"expires_at\":\"2026-12-31T23:59:59Z\"}"
}],
"isError": false
}
}Workflow 5: Search The Product Catalog
Use an MCP-visible catalogue tool to discover products by keyword. Requires products:read scope.
{
"jsonrpc": "2.0", "id": "w5", "method": "tools/call",
"params": {
"name": "list_products",
"arguments": {
"search": "enterprise",
"limit": 10
}
}
}{
"jsonrpc": "2.0", "id": "w5",
"result": {
"content": [{
"type": "text",
"text": "{\"products\":[{\"product_id\":\"prod_platform_pro\",\"name\":\"Platform Pro (Enterprise)\",\"price\":4999.00},{\"product_id\":\"prod_support_plus\",\"name\":\"Support Plus\",\"price\":999.00}]}"
}],
"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": "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" },
{ "uri": "salesbooth://billing/credits", "name": "Credit Balance", "mimeType": "application/json" },
{ "uri": "salesbooth://trust/score-history", "name": "Trust Score History", "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 Detail", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://products/{product_id}", "name": "Product Detail", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://customers/{customer_id}", "name": "Customer Detail", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://contracts/{contract_id}", "name": "Contract Detail", "mimeType": "application/json" },
{ "uriTemplate": "salesbooth://templates/{template_id}", "name": "Template Detail", "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.",
"arguments": [
{ "name": "customer_id", "description": "Existing customer ID (optional — will prompt for creation if not provided)", "required": false },
{ "name": "product_ids", "description": "Comma-separated product IDs to include in the deal", "required": false },
{ "name": "currency", "description": "Deal currency (ISO 4217, e.g. USD, EUR). Defaults to tenant default.", "required": false }
]
},
{
"name": "negotiate-deal",
"description": "Structured negotiation workflow: review current deal terms, apply intelligence scoring, propose or counter terms.",
"arguments": [
{ "name": "deal_id", "description": "The deal ID to negotiate on", "required": true },
{ "name": "strategy", "description": "Negotiation strategy hint: \"aggressive\", \"balanced\", or \"conservative\"", "required": false }
]
},
{
"name": "configure-widget",
"description": "Widget setup workflow: select a site, choose products, configure theme and steps, generate embed code.",
"arguments": [
{ "name": "site_id", "description": "Site ID to attach the widget to", "required": false },
{ "name": "product_ids", "description": "Comma-separated product IDs to feature in the widget", "required": false }
]
},
{
"name": "review-pipeline",
"description": "Pipeline health check: analyze deal distribution by status, identify bottlenecks, get recommended actions.",
"arguments": [
{ "name": "focus", "description": "Focus area: \"conversion\", \"revenue\", \"velocity\", or \"all\"", "required": false }
]
},
{
"name": "batch-deal-workflow",
"description": "Atomic multi-step deal workflow: create deal, add line items, apply discount, and transition status in a single all-or-nothing transaction. Use this for autonomous agent deal execution to avoid orphaned deals on partial failure.",
"arguments": [
{ "name": "customer_id", "description": "Existing customer ID for the deal", "required": true },
{ "name": "product_ids", "description": "Comma-separated product IDs to add as line items", "required": false },
{ "name": "discount_percent", "description": "Optional discount percentage to apply (0-100)", "required": false },
{ "name": "target_status", "description": "Target deal status after creation: \"in_progress\" (default) or \"pending_signature\"", "required": false }
]
},
{
"name": "manage-delegation",
"description": "Create or update agent delegations: define scope, spending limits, and expiry for sub-agents.",
"arguments": [
{ "name": "grantee_agent_key_id", "description": "API key ID of the agent receiving delegation", "required": false },
{ "name": "max_spend", "description": "Maximum spending limit for the delegation", "required": false }
]
}
]
}
}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": {
"description": "Structured negotiation workflow: review current deal terms, apply intelligence scoring, propose or counter terms.",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "You are negotiating deal `deal_xyz789` using a **balanced** strategy. Follow these steps:\n\n1. **Review current deal**: Use `check_deal_status` with id = `deal_xyz789` to see current terms, line items, and pricing.\n2. **Get intelligence**: Use `score_deal` with deal_id = `deal_xyz789` for AI scoring and pricing recommendations.\n3. **Check negotiation history**: Use `get_negotiation_history` with deal_id = `deal_xyz789` to see prior proposals and counters.\n4. **Propose or counter terms**: Based on the balanced strategy:\n - **aggressive**: Push for maximum value; counter with minimal concessions.\n - **balanced**: Find middle ground; match concessions proportionally.\n - **conservative**: Prioritize closing; offer reasonable discounts.\n Use `negotiate_terms` to submit your proposal.\n5. **Monitor response**: Check deal status for acceptance or further counter-proposals."
}
}
]
}
}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": "cross_instance_discover",
"arguments": { "instance": "partner.salesbooth.com" }
}
}{
"jsonrpc": "2.0", "id": "f2", "method": "tools/call",
"params": {
"name": "cross_instance_negotiate",
"arguments": {
"target_instance": "partner.salesbooth.com",
"type": "propose",
"payload": { "deal_id": "deal_xyz789", "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-discovery.json) 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 for GET status/audit and POST ?action=discover; agent:negotiate for POST ?action=send and ?action=negotiate; agent:execute for POST ?action=escrow, ?action=settle, and ?action=dispute.
Authentication: Requests are signed end-to-end with tenant Ed25519 key pairs. Remote instances validate incoming envelopes against the originator’s DID document at the issuer host’s /.well-known/did.json.
Actions (POST)
GET Actions
curl https://api.salesbooth.com/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl -X POST "https://api.salesbooth.com/v1/federation/negotiate?action=discover" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"instance": "partner.salesbooth.com",
"filters": { "category": "software", "currency": "USD" }
}'curl -X POST "https://api.salesbooth.com/v1/federation/negotiate?action=send" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"target_instance": "partner.salesbooth.com",
"target_agent_id": "agent_partner_123",
"type": "propose",
"payload": {
"deal_id": "deal_remote_123",
"proposed_terms": { "discount_percent": 10, "currency": "USD" },
"expires_at": ""
}
}' 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://api.salesbooth.com/v1/federation/negotiate \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/v1/federation/negotiate?action=audit&xref_id=xref_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Query parameter: action is required in the URL for POST requests, for example ?action=discover or ?action=send.
| Field | Type | Description |
|---|---|---|
| instance | string | Remote instance hostname for action=discover (for example partner.salesbooth.com) |
| filters | object | Optional discovery filters for action=discover, such as category or currency |
| target_instance | string | Remote instance hostname for action=send |
| target_agent_id | string | Optional remote agent identifier for action=send |
| type | string | Envelope type for action=send, such as propose, counter, accept, or reject |
| payload | object | Envelope payload for action=send |
curl -X POST "https://api.salesbooth.com/v1/federation/negotiate?action=discover" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"instance": "partner.salesbooth.com",
"filters": { "category": "software", "currency": "USD" }
}'curl -X POST "https://api.salesbooth.com/v1/federation/negotiate?action=send" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"target_instance": "partner.salesbooth.com",
"target_agent_id": "agent_partner_123",
"type": "propose",
"payload": {
"deal_id": "deal_remote_123",
"proposed_terms": { "discount_percent": 10, "currency": "USD" },
"expires_at": ""
}
}' 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-discovery.json| Parameter | Type | Description |
|---|---|---|
| tenant_id required | string | Tenant whose federation manifest to retrieve |
curl "https://api.salesbooth.com/v1/federation/discovery?tenant_id=tenant_xxxxx"{
"error": false,
"success": true,
"data": {
"protocol": "salesbooth-negotiate/1.0",
"instance": {
"tenant_id": "tenant_xxxxx",
"name": "Acme Renovations",
"version": "1.0"
},
"capabilities": {
"discovery": true,
"negotiation": true,
"escrow": true,
"signatures": true
},
"catalog": {
"product_count": 24,
"deal_count": 8,
"currencies": ["USD", "AUD"]
},
"endpoints": {
"discovery": "/api/v1/deal-discovery",
"negotiate": "/api/v1/federation/negotiate",
"public_key": "/api/v1/tenant/public-key"
},
"rate_limits": {
"discovery_per_minute": 30,
"negotiate_per_minute": 10
},
"updated_at": "2026-07-02T02:12:05Z",
"signing": {
"algorithm": "ed25519",
"key_id": "tsk_abc123def456",
"public_key": "MCowBQYDK2VwAyEA7H4x5nN5mN7N8Qd3r2WjJ3l4J5k6L7m8N9p0Qr1sT2U="
}
}
}| 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://api.salesbooth.com/v1/tenant/public-key?tenant_id=tenant_xxxxx"{
"error": false,
"success": true,
"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: Signed cross-instance federation envelopes require the sending agent to be at least Trust Level 1 (Provisional). 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. If a specific federation action requires a higher trust level, that stricter gate must be enforced and documented on the endpoint itself.
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.
tenant_id; the alternate /.well-known/did.json path only works on a host that maps to a single tenant domain. Includes the tenant’s Ed25519 verification method and service endpoints for federation, deal verification, and key rotation.| Parameter | Type | Description |
|---|---|---|
| tenant_id | string | Tenant whose DID document to retrieve on the canonical API route. This parameter is required for /api/v1/did-document. |
curl "https://api.salesbooth.com/v1/did-document?tenant_id=tenant_xxxxx"{
"error": false,
"success": true,
"data": {
"@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://api.salesbooth.com/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://api.salesbooth.com/v1/federation-peers \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/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,
"success": true,
"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://api.salesbooth.com/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, "success": true, "data": { "updated": true, "id": 1 } }agent:execute scope.curl -X DELETE "https://api.salesbooth.com/v1/federation-peers?id=1" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{ "error": false, "success": true, "data": { "deleted": true, "id": 1 } }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 validation_error.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://api.salesbooth.com/v1/sandbox/reset \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/sandbox/seed \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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_type 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://api.salesbooth.com/v1/sandbox/simulate_webhook \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"event_type": "deal.closed",
"data": {
"deal_id": "deal_test_abc123",
"total": "2500.00",
"currency": "USD"
}
}'{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/sandbox/status \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/playground/session \
-H "Content-Type: application/json"{
"error": false,
"success": true,
"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 | Use the key_prefix returned by POST /api/v1/playground/session (first 12 characters of the playground API key, min 8 chars) |
curl "https://api.salesbooth.com/v1/playground/session?key_prefix=sb_test_xxxx"{
"error": false,
"success": true,
"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://api.salesbooth.com/v1/widget-config \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl "https://api.salesbooth.com/v1/widget-config?api_key=sb_pub_xxxxxxxxxxxx"{
"error": false,
"success": true,
"data": {
"widget_id": "wgt_xxxxxxxxxxxx",
"title": "Get Started",
"theme_color": "#2563eb",
"currency": "USD",
"merchant_available": true,
"stripe_publishable_key": "pk_live_xxxxxxxxxxxx",
"products": "prod_xxxxxxxxxxxx"
}
}| Parameter | Description |
|---|---|
| id required | Widget ID to retrieve |
curl "https://api.salesbooth.com/v1/widget-config?id=wgt_xxxxxxxxxxxx" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"widget_id": "wgt_xxxxxxxxxxxx",
"site_id": "site_xxxxxxxxxxxx",
"title": "Get Started",
"theme_color": "#2563eb",
"currency": "USD",
"products": "prod_xxxxxxxxxxxx",
"api_key": "sb_pub_xxxxxxxxxxxx",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z"
}
}widget_id; the authenticated id selector is not used in this mode.| Parameter | Description |
|---|---|
| api_key | Publishable API key (sb_pub_*) used to load the widget configuration tied to that key. Can also be sent via the Authorization: Bearer or X-API-Key header. |
| widget_id | Public widget ID lookup for callers that need a specific widget configuration without using the authenticated id selector. |
curl "https://api.salesbooth.com/v1/widget-config?api_key=sb_pub_xxxxxxxxxxxx"
curl "https://api.salesbooth.com/v1/widget-config?widget_id=wgt_xxxxxxxxxxxx"
curl "https://api.salesbooth.com/v1/widget-config?widget_id=wgt_xxxxxxxxxxxx" \
-H "Authorization: Bearer sb_pub_xxxxxxxxxxxx"| Field | Description |
|---|---|
| Basic | |
| site_id required | The site to attach the widget to |
| title | Widget display title (default: “Create a Deal”) |
| theme_color | Primary theme colour as CSS hex (default: #2563eb) |
| currency | ISO 4217 settlement currency code (default: USD). One of: USD, EUR, GBP, CAD, AUD, NZD, CHF, JPY, MXN, BRL |
| products | Comma-separated product IDs available in the widget |
| cta_text | Custom call-to-action button label |
| Appearance | |
| dark_mode | Boolean — enable dark colour scheme |
| logo_url | Merchant logo URL displayed in widget header |
| font_family | Font family name (web-safe or Google Font) |
| custom_css | Custom CSS injected into widget Shadow DOM (sanitised server-side) |
| border_radius | Border radius in pixels, 0–16 (default: 12) |
| button_style | CTA button style: filled, outlined, or text |
| Negotiation | |
| enable_negotiation | Boolean — allow buyers to submit counter-offers |
| negotiation_mode | Negotiation scope: price_only, item_selection, terms_only, or full |
| auto_accept_threshold | Minimum counter-offer percentage (0–100) to auto-accept |
| Payment | |
| payment_type | Payment collection mode: full, deposit, or quote |
| deposit_type | Deposit calculation method: fixed or percentage |
| deposit_value | Deposit amount (when fixed) or percentage 0–100 (when percentage) |
| deposit_note | Note about the remaining balance shown to the buyer |
| Steps | |
| step_1_name | Custom semantic label for the Products step |
| step_2_name | Custom semantic label for the Configure step |
| step_3_name | Custom semantic label for the Summary step |
| step_4_name | Custom semantic label for the Negotiate step (8-step flow) |
| step_5_name | Custom semantic label for the Customer/Details step (8-step flow) |
| step_6_name | Custom semantic label for the Terms step (8-step flow) |
| step_7_name | Custom semantic label for the Payment step (8-step flow) |
| step_8_name | Custom semantic label for the Confirmation step (8-step flow) |
| booking_step_name | Custom semantic label for the Booking step when it is active |
| discount_step_name | Custom semantic label for the Discount step when it is active |
| step_order | Custom step ordering: comma-separated step names (e.g. products,summary,customer,payment,confirmation) |
| skip_steps | Steps to skip from default flow: comma-separated step names (payment and confirmation cannot be skipped) |
| step_conditions | Conditional step visibility rules: JSON object mapping step names to conditions (minTotal, maxTotal, dealType) |
| Promotions | |
| enable_promo_codes | Boolean — allow buyers to enter promotional codes |
| enable_discount_step | Boolean — enable a dedicated discount step in the widget flow |
| auto_discounts | Automatic discount rules based on cart contents or quantity thresholds (object) |
| Templates | |
| contract_template_id | Contract template used for terms shown to buyers |
| deal_template_id | Deal template used to pre-populate the widget |
| Social Proof | |
| show_social_proof | Boolean — show purchase count indicators (default: true) |
| show_smart_defaults | Boolean — pre-select popular options as defaults (default: true) |
| social_proof_threshold | Minimum purchase count before social proof displays (1–100, default: 5) |
| show_stock_quantity | Boolean — display remaining stock quantity next to products |
| Analytics | |
| analytics_mode | Comma-separated analytics targets: gtm, ga4, segment, callback |
| analytics_callback | Global JS function name for custom analytics events |
| analytics_consent | Boolean — require user consent before firing analytics (GDPR) |
| Localisation | |
| locale | Widget UI language code (e.g. en, en-AU, es, de, fr, ja) |
| locale_overrides | Custom string overrides for widget i18n labels (object with dot-separated keys) |
| display_currencies | ISO 4217 currency codes available for buyer display conversion (array of strings) |
| Product Selection | |
| product_selection_mode | Whether buyers can select one or multiple products: single or multiple (default: single) |
| saved_config_ttl_days | Days a buyer’s saved configuration is retained (1–365, default: 30) |
| Booking | |
| booking_config | Widget-level booking step configuration object: enable_booking, staff_ids, duration, availability_source, custom_hours, buffer_time, max_advance_days, confirmation |
| Network & Advanced | |
| network_config | Network and cache tuning object: fetch_timeout, retry_max, retry_base_delay, retry_max_delay, retry_jitter, total_retry_budget, cache_products_ttl, cache_config_ttl, cache_assets_ttl, negotiate_poll_interval, negotiate_sse_timeout, heartbeat_interval, heartbeat_offline_interval, abandon_timeout (milliseconds except retry_jitter). Legacy aliases timeout_ms, retry_attempts, max_retries, retry_delay_ms, and cache_ttl_seconds are still normalized for backward compatibility. |
| signature_mode | Contract signature capture method: checkbox, type, draw, or digital (default: checkbox) |
| Field | Description |
|---|---|
| Basic | |
| title | Widget display title |
| theme_color | Primary theme colour as CSS hex |
| currency | ISO 4217 settlement currency code. One of: USD, EUR, GBP, CAD, AUD, NZD, CHF, JPY, MXN, BRL |
| products | Comma-separated product IDs available in the widget |
| cta_text | Custom call-to-action button label |
| Appearance | |
| dark_mode | Boolean — enable dark colour scheme |
| logo_url | Merchant logo URL displayed in widget header |
| font_family | Font family name (web-safe or Google Font) |
| custom_css | Custom CSS injected into widget Shadow DOM (sanitised server-side) |
| border_radius | Border radius in pixels, 0–16 (default: 12) |
| button_style | CTA button style: filled, outlined, or text |
| Negotiation | |
| enable_negotiation | Boolean — allow buyers to submit counter-offers |
| negotiation_mode | Negotiation scope: price_only, item_selection, terms_only, or full |
| auto_accept_threshold | Minimum counter-offer percentage (0–100) to auto-accept |
| Payment | |
| payment_type | Payment collection mode: full, deposit, or quote |
| deposit_type | Deposit calculation method: fixed or percentage |
| deposit_value | Deposit amount (when fixed) or percentage 0–100 (when percentage) |
| deposit_note | Note about the remaining balance shown to the buyer |
| Steps | |
| step_1_name | Custom semantic label for the Products step |
| step_2_name | Custom semantic label for the Configure step |
| step_3_name | Custom semantic label for the Summary step |
| step_4_name | Custom semantic label for the Negotiate step (8-step flow) |
| step_5_name | Custom semantic label for the Customer/Details step (8-step flow) |
| step_6_name | Custom semantic label for the Terms step (8-step flow) |
| step_7_name | Custom semantic label for the Payment step (8-step flow) |
| step_8_name | Custom semantic label for the Confirmation step (8-step flow) |
| booking_step_name | Custom semantic label for the Booking step when it is active |
| discount_step_name | Custom semantic label for the Discount step when it is active |
| step_order | Custom step ordering: comma-separated step names (e.g. products,summary,customer,payment,confirmation) |
| skip_steps | Steps to skip from default flow: comma-separated step names (payment and confirmation cannot be skipped) |
| step_conditions | Conditional step visibility rules: JSON object mapping step names to conditions (minTotal, maxTotal, dealType) |
| Promotions | |
| enable_promo_codes | Boolean — allow buyers to enter promotional codes |
| enable_discount_step | Boolean — enable a dedicated discount step in the widget flow |
| auto_discounts | Automatic discount rules based on cart contents or quantity thresholds (object) |
| Templates | |
| contract_template_id | Contract template used for terms shown to buyers |
| deal_template_id | Deal template used to pre-populate the widget |
| Social Proof | |
| show_social_proof | Boolean — show purchase count indicators |
| show_smart_defaults | Boolean — pre-select popular options as defaults |
| social_proof_threshold | Minimum purchase count before social proof displays (1–100) |
| show_stock_quantity | Boolean — display remaining stock quantity next to products |
| Analytics | |
| analytics_mode | Comma-separated analytics targets: gtm, ga4, segment, callback |
| analytics_callback | Global JS function name for custom analytics events |
| analytics_consent | Boolean — require user consent before firing analytics (GDPR) |
| Localisation | |
| locale | Widget UI language code (e.g. en, en-AU, es, de, fr, ja) |
| locale_overrides | Custom string overrides for widget i18n labels (object with dot-separated keys) |
| display_currencies | ISO 4217 currency codes available for buyer display conversion (array of strings) |
| Product Selection | |
| product_selection_mode | Whether buyers can select one or multiple products: single or multiple |
| saved_config_ttl_days | Days a buyer’s saved configuration is retained (1–365) |
| Booking | |
| booking_config | Widget-level booking step configuration object: enable_booking, staff_ids, duration, availability_source, custom_hours, buffer_time, max_advance_days, confirmation |
| Network & Advanced | |
| network_config | Network and cache tuning object: fetch_timeout, retry_max, retry_base_delay, retry_max_delay, retry_jitter, total_retry_budget, cache_products_ttl, cache_config_ttl, cache_assets_ttl, negotiate_poll_interval, negotiate_sse_timeout, heartbeat_interval, heartbeat_offline_interval, abandon_timeout (milliseconds except retry_jitter). Legacy aliases timeout_ms, retry_attempts, max_retries, retry_delay_ms, and cache_ttl_seconds are still normalized for backward compatibility. |
| signature_mode | Contract signature capture method: checkbox, type, draw, or digital |
curl -X PATCH "https://api.salesbooth.com/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://api.salesbooth.com/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) |
curl -X POST https://api.salesbooth.com/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" }'{
"error": false,
"success": true,
"data": {
"title": "Enterprise Starter Pack",
"cta_text": "Get Started",
"step_1_name": "Build Your Bundle",
"step_2_name": "Configure",
"step_3_name": "Review & Submit",
"step_4_name": "Negotiate",
"step_5_name": "Details",
"step_6_name": "Terms",
"step_7_name": "Payment",
"step_8_name": "Done",
"payment_type": "full",
"products": "prod_1, prod_2",
"currency": "USD",
"deal_template_id": "dtpl_abc123",
"template_name": "Enterprise Starter Pack"
}
}| Field | Type | Description |
|---|---|---|
| product_ids required | array | Array of product ID strings to base the configuration on |
curl -X POST "https://api.salesbooth.com/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,
"success": true,
"data": {
"title": "Build Your Widgets Package",
"cta_text": "Configure & Order",
"step_1_name": "Build Your Bundle",
"step_2_name": "Configure",
"step_3_name": "Summary",
"step_4_name": "Negotiate",
"step_5_name": "Details",
"step_6_name": "Terms",
"step_7_name": "Payment",
"step_8_name": "Done",
"payment_type": "full",
"locale": "en"
}
}<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://api.salesbooth.com/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 = Salesbooth.init({ 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', null, { 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 and conversions are limited to 10 per minute 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. Each item has product_id, name, quantity, configured_price, and an optional configuration object containing the selected option and modifier state for that product |
| 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) |
| customer_phone | Buyer phone number (optional) |
| customer_custom_fields | Object of custom field key-value pairs collected from the buyer during configuration (optional) |
| booking_selections | Object with booking slot selections (optional) |
| expires_in | Expiration in days, 1–365 (default: 30) |
| widget_id | Widget ID to associate this configuration with. Used to resolve TTL and display settings. Inferred from the publishable key context; supply explicitly when authenticating with a secret API key (optional) |
curl -X POST https://api.salesbooth.com/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://api.salesbooth.com/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": [
{
"product_id": "prod_renovation_consult",
"quantity": 1,
"option_ids": ["opt_priority_booking"]
}
],
"pricing_snapshot": {
"subtotal": 99,
"discount_total": 0,
"tax_total": 0,
"total": 99
},
"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 |
| customer_phone | Buyer phone number |
| option_selections | Object mapping product IDs to their selected option values |
| booking_selections | Object with booking slot selections |
curl -X PATCH "https://api.salesbooth.com/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_test_* or sb_live_*). Publishable keys are not accepted for this operation.curl -X DELETE "https://api.salesbooth.com/v1/saved-configs?id=a3f8b21c" \
-H "Authorization: Bearer sb_test_xxxxx"customer_name and customer_email only to override saved buyer details; conversion still needs an effective name and email from either the saved configuration or the request body. Returns 409 if already converted, 410 if expired.| Field | Description |
|---|---|
| short_code required | The short code of the saved configuration |
| customer_name | Optional buyer name override; falls back to the saved value when omitted |
| customer_email | Optional buyer email override; falls back to the saved value when omitted |
| customer_phone | Buyer phone number |
curl -X POST https://api.salesbooth.com/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": {
"deal_id": "deal_xxxxx",
"status": "draft",
"currency": "USD",
"total": 99
},
"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 | string | Widget publishable API key. Optional when using Authorization: Bearer sb_pub_*. |
| session_id required | string | Unique session identifier |
| event_type required | string | Event type: impression, step-change, product-selected, customer-entered, payment-started, deal-created, payment-completed, abandoned, error, terms-accepted, transition-warning |
| step | string | Current step name |
| data | object | Additional event data |
curl -X POST https://api.salesbooth.com/v1/widget-analytics \
-H "Content-Type: application/json" \
-d '{
"api_key": "sb_pub_xxxxx",
"session_id": "sess_abc123",
"event_type": "step-change",
"step": "product_selection",
"data": {
"from": "landing",
"to": "product_selection",
"stepName": "Product selection"
}
}'widget_id to return tenant-wide analytics for the authenticated tenant.| Parameter | Type | Description |
|---|---|---|
| widget_id | string | Optional widget identifier filter. Omit to return tenant-wide analytics. |
| days | integer | Optional lookback period in days (default: 30) |
curl "https://api.salesbooth.com/v1/widget-analytics?widget_id=wgt_xxxxx&days=7" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"Session replay collection
Use the replay collector for browser session recordings and event playback tied to a tenant website domain. This endpoint is public, but each request must identify the originating host so Salesbooth can resolve the correct site.
host that matches a configured site domain and returns 204 No Content on success.| Field | Type | Description |
|---|---|---|
| session_id required | string | Client session identifier used to append events to the same replay stream |
| enc required | string | Encoding marker. Must be b64. |
| data required | string | Base64-encoded JSON object containing an events array |
| host required | string | Customer website domain the replay originated from |
| timezone | string | Optional client timezone stored with the replay session |
curl -X POST https://api.salesbooth.com/v1/collect \
-H "Content-Type: application/json" \
-d '{
"session_id": "sess_abc123",
"enc": "b64",
"host": "example.com",
"data": "eyJldmVudHMiOlt7InR5cGUiOiJwYWdlX3ZpZXciLCJ0cyI6MTcyMDEzMDAwMH1dfQ==",
"timezone": "Australia/Sydney"
}'204 accepted, 400 invalid payload, 404 unknown host, 413 body too large, 429 rate limited, and 500 write failure.Uploads
Upload image files for products, option swatches, and other assets.
curl -X POST https://api.salesbooth.com/v1/uploads \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-F "file=@product-image.jpg"{
"error": false,
"success": true,
"data": {
"url": "/uploads/tenant_abc123/7f3c9a12d4e6b8c0f1a2d3e4b5c6d7e8.jpg",
"filename": "7f3c9a12d4e6b8c0f1a2d3e4b5c6d7e8.jpg",
"size": 145280,
"mime_type": "image/jpeg"
}
}Credits
Prepaid credit balance management. Check balance, top up via hosted checkout, view ledger history, and estimate deal costs.
curl https://api.salesbooth.com/v1/credits \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"balance": 5000,
"low_balance_threshold": 100,
"is_low": false,
"can_create_deals": true,
"cost_per_deal": 1
}
}| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max results (default: 25) |
| offset | integer | Pagination offset |
| type | string | Filter: credit or debit |
curl "https://api.salesbooth.com/v1/credits?action=ledger&limit=10" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"billing:read scope.curl https://api.salesbooth.com/v1/credits?action=summary \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"credit_balance": 5000,
"trust_level": 1,
"trust_level_label": "Verified",
"auto_topup_enabled": false,
"auto_topup_ceiling": null,
"cost_per_deal": 1,
"estimated_remaining_deals": 5000,
"can_create_deals": true
}
}| Field | Type | Description |
|---|---|---|
| amount required | number | Top-up amount in USD (min: $5, max: $10,000) |
| success_url required | string | Redirect URL on success |
| cancel_url required | string | Redirect URL on cancel |
curl -X POST https://api.salesbooth.com/v1/credits?action=topup \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "amount": 100, "success_url": "https://example.com/success", "cancel_url": "https://example.com/cancel" }'curl -X POST https://api.salesbooth.com/v1/credits?action=estimate \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'billing:write scope.| Field | Type | Description |
|---|---|---|
| threshold required | number | Low-balance warning threshold in USD |
curl -X POST https://api.salesbooth.com/v1/credits?action=set-threshold \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "threshold": 250 }'USDC
USDC is the tenant prepaid balance rail for agent spending on Base. It is separate from Stripe card collection and supports wallet setup, Coinbase-hosted deposits, and transaction history.
action=transactions. Requires billing:read; tenant session auth is also supported.| Parameter | Type | Description |
|---|---|---|
| action | string | balance (default) or transactions |
| page | integer | Transactions page number (default: 1) |
| per_page | integer | Transactions page size (default: 20, max: 100) |
curl "https://api.salesbooth.com/v1/usdc?action=balance" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"action=balance returns data.usdc_balance, data.wallet_address, chain metadata, and data.recent_charges[]. action=transactions returns data.transactions[] with pagination fields. Common errors: 400, 401, 403, 405, 413, and 500.billing:write; tenant session auth is also supported.| Field / Parameter | Type | Description |
|---|---|---|
| action required | query string | create-charge or set-wallet |
| amount | number | Required for create-charge. Deposit amount in USDC, subject to the tenant minimum deposit. |
| wallet_address | string | Required for set-wallet. Ethereum wallet address (0x + 40 hex chars). |
curl -X POST "https://api.salesbooth.com/v1/usdc?action=create-charge" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "amount": 25 }'curl -X POST "https://api.salesbooth.com/v1/usdc?action=set-wallet" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "wallet_address": "0x1234567890abcdef1234567890abcdef12345678" }'201 for create-charge with charge_id, charge_code, hosted_url, amount_usdc, and expires_at; 200 for set-wallet with the normalised wallet_address. Common errors: 400, 401, 402, 403, 405, 413, 503, and 500.Billing
Billing dashboard endpoints for credit balance, usage charts, transaction ledger, invoices, auto top-up configuration, and alert settings.
curl https://api.salesbooth.com/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://api.salesbooth.com/v1/billing?action=usage-daily&days=14" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"action=setup-intent to save a saved payment method, then submit the resulting payment_method_id.| Field | Type | Description |
|---|---|---|
| enabled required | boolean | Enable or disable auto top-up |
| amount | integer | Credits to purchase on trigger |
| payment_method_id | string | Saved saved payment method ID from a completed setup intent |
curl -X POST https://api.salesbooth.com/v1/billing?action=auto-topup \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "enabled": true, "amount": 500, "payment_method_id": "pm_123456789" }'| Field | Type | Description |
|---|---|---|
| threshold | integer | Alert when balance drops below this amount |
| email_enabled | boolean | Enable or disable email alerts |
| webhook_enabled | boolean | Enable or disable webhook alerts |
curl -X POST https://api.salesbooth.com/v1/billing?action=alert-settings \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "threshold": 50, "email_enabled": true }'| Parameter | Type | Description |
|---|---|---|
| limit | integer | Number of entries per page (default: 25, max: 100) |
| offset | integer | Offset for pagination (default: 0) |
| type | string | Filter by entry type: credit or debit |
curl "https://api.salesbooth.com/v1/billing?action=ledger&limit=25&offset=0" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| type | string | Filter by entry type: credit or debit |
curl "https://api.salesbooth.com/v1/billing?action=export-ledger" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| limit | integer | Number of invoices per page (default: 25, max: 100) |
| offset | integer | Offset for pagination (default: 0) |
curl "https://api.salesbooth.com/v1/billing?action=invoices&limit=25" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"text/html document suitable for browser rendering, printing, or PDF generation.| Parameter | Type | Description |
|---|---|---|
| id required | string | Invoice ID to download |
curl "https://api.salesbooth.com/v1/billing?action=invoice-pdf&id=inv_abc123" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Parameter | Type | Description |
|---|---|---|
| limit | integer | Number of entries per page (default: 25, max: 100) |
| offset | integer | Offset for pagination (default: 0) |
curl "https://api.salesbooth.com/v1/billing?action=auto-topup-log&limit=25" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"| Field | Type | Description |
|---|---|---|
| amount required | number | Top-up amount in USD (min: $5, max: $10,000) |
| success_url required | string | URL to redirect to after successful payment |
| cancel_url required | string | URL to redirect to if payment is cancelled |
curl -X POST https://api.salesbooth.com/v1/billing?action=topup \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "amount": 50, "success_url": "https://app.example.com/billing?topup=success", "cancel_url": "https://app.example.com/billing" }'curl -X POST https://api.salesbooth.com/v1/billing?action=setup-intent \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{}'Team
Manage team members and roles. Invite users, update roles, and remove team members.
curl https://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/v1/team \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{ "membership_id": 42, "role": "admin" }'curl -X DELETE "https://api.salesbooth.com/v1/team?id=42" \
-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://api.salesbooth.com/v1/trust \
-H "Authorization: Bearer sb_test_example_key_do_not_use"{
"error": false,
"success": true,
"data": {
"trust_level": 2,
"trust_label": "Established",
"trust_score": 85,
"trust_level_changed_at": "2026-06-12 09:30:00",
"ceiling": 10000,
"auto_topup": {
"enabled": true,
"amount": 500,
"ceiling": 10000,
"monthly_used": 1500,
"monthly_remaining": 8500
},
"progress": {
"next_level": 3,
"next_label": "Trusted",
"next_ceiling": 500,
"requirements": {
"score": { "current": 85, "required": 150, "met": false },
"days_active": { "current": 45, "required": 90, "met": false },
"deals_closed": { "current": 12, "required": 50, "met": false },
"max_disputes": { "required": 5 }
}
}
}
}| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max results (default: 50) |
| offset | integer | Pagination offset |
curl https://api.salesbooth.com/v1/trust?action=history \
-H "Authorization: Bearer sb_test_example_key_do_not_use"tenant_id. Remote instances call this during federation to verify that incoming credentials have not been revoked since issuance.| Parameter | Type | Description |
|---|---|---|
| tenant_id | string | Required tenant ID whose revocation list should be returned |
curl "https://api.salesbooth.com/v1/trust/revocations?tenant_id=tenant_abc123"{
"error": false,
"success": true,
"data": {
"issuer": "did:web:example.salesbooth.com",
"type": "AgentTrustRevocationList",
"revoked_credentials": [
{
"credential_id": "cred_abc123",
"revoked_at": "2026-02-10T08:00:00Z",
"revocation_reason": "trust_demotion"
}
],
"count": 1,
"updated_at": "2026-03-18T12:00:00Z"
}
}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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/v1/gdpr/consent_expiring?days_ahead=14" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://api.salesbooth.com/v1/gdpr/consent_health \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/v1/gdpr/dashboard \
-H "Authorization: Bearer sb_test_example_key_do_not_use"curl https://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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://api.salesbooth.com/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",
"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. Allowed values are verified, broken, and unavailable. A broken status triggers an immediate security review.
Webhook Signature Verification
Every webhook delivery includes an X-Salesbooth-Signature header (HMAC-SHA256 of timestamp + "." + raw body) and an X-Salesbooth-Timestamp header (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
const expectedBuf = Buffer.from(expected);
const signatureBuf = Buffer.from(signature);
if (expectedBuf.length !== signatureBuf.length || !crypto.timingSafeEqual(expectedBuf, signatureBuf)) {
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 the
X-Salesbooth-Delivery-Idheader to 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://api.salesbooth.com/v1/customer-auth?action=request-access \
-H "Content-Type: application/json" \
-d '{ "email": "customer@example.com" }'{
"error": false,
"success": true,
"data": {
"success": true,
"message": "If an account exists with this email, a login link has been sent."
}
}| Field | Type | Description |
|---|---|---|
| token required | string | One-time token from the magic link |
curl -X POST https://api.salesbooth.com/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",
"token_type": "Bearer",
"expires_in": 3600
}
}| Field | Type | Description |
|---|---|---|
| refresh_token required | string | Refresh token from the verify response |
Authorization: Bearer <customer_jwt> and omit the JSON body.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 the payment form 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 | payment intent ID |
| Field | Type | Description |
|---|---|---|
| name | string | Display name |
| string | Email address | |
| phone | string | Phone number |
| company | string | Company name |
| city | string | City |
| state | string | State or region |
| zip | string | Postal code |
| country | string | Country |
| Parameter | Type | Description |
|---|---|---|
| deal_id required | string | Deal the customer is accessing |
curl "https://api.salesbooth.com/v1/customer-portal?action=portal-config&deal_id=deal_xxxxx" \
-H "Authorization: Bearer <customer_jwt>"{
"error": false,
"success": true,
"data": {
"stripe_publishable_key": "pk_test_xxxxx",
"payment_enabled": true
}
}curl "https://api.salesbooth.com/v1/customer-portal?action=consents" \
-H "Authorization: Bearer <customer_jwt>"{
"error": false,
"success": true,
"data": {
"consents": [
{
"id": "con_xxxxx",
"purpose": "marketing",
"granted_at": "2025-12-01T10:00:00Z",
"expires_at": "2026-12-01T10:00:00Z",
"is_expired": false,
"renewal_required": false
}
]
}
}deal.change_requested webhook event is dispatched on success.| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to request changes for |
| message | string | Description of the requested changes |
curl -X POST "https://api.salesbooth.com/v1/customer-portal?action=request-changes" \
-H "Authorization: Bearer <customer_jwt>" \
-H "Content-Type: application/json" \
-d '{ "deal_id": "deal_xxxxx", "message": "Please adjust the payment schedule to monthly." }'{
"error": false,
"success": true,
"data": {
"deal_id": "deal_xxxxx",
"message": "Change request submitted"
}
}| Field | Type | Description |
|---|---|---|
| purpose required | string | Consent purpose to renew (e.g. marketing, data-processing) |
curl -X POST "https://api.salesbooth.com/v1/customer-portal?action=consent-renew" \
-H "Authorization: Bearer <customer_jwt>" \
-H "Content-Type: application/json" \
-d '{ "purpose": "marketing" }'{
"error": false,
"success": true,
"data": {
"id": "con_xxxxx",
"purpose": "marketing",
"granted_at": "2026-05-16T10:00:00Z",
"expires_at": "2027-05-16T10:00:00Z"
}
}curl -X POST "https://api.salesbooth.com/v1/customer-portal?action=consent-renew-all" \
-H "Authorization: Bearer <customer_jwt>"{
"error": false,
"success": true,
"data": {
"renewed_count": 3,
"consents": [
{ "purpose": "marketing", "expires_at": "2027-05-16T10:00:00Z" },
{ "purpose": "data-processing", "expires_at": "2027-05-16T10:00:00Z" }
]
}
}Deal Discovery
Enables AI agents to search and browse available deal offers programmatically. 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 AI assistant 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://api.salesbooth.com/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",
"status": "in_progress",
"total": "2999.00",
"currency": "USD",
"category": "software",
"structured_terms": null,
"line_item_count": 3,
"metadata": null,
"created_at": "2026-03-15T10:30:00Z"
}
],
"pagination": { "total": 42, "limit": 50, "offset": 0, "count": 1 }
}
}Structured deal terms schema: Use GET /api/v1/schema/deal-terms from Machine-Readable Schemas when validating deal_terms payloads for negotiation and set-terms flows.
Quotes & Invoices
Generate shareable quotes and invoices from deals. Quotes have a configurable validity window; invoices are permanent records. Customer-facing quote access happens through the public share_url viewer or the read-only short-code lookup endpoint, while seller API retrieval and listing stay behind authenticated quote endpoints.
Required scope: deals:write to generate quotes and invoices. Seller API quote listing and retrieval require deals:read. Anonymous customer quote reads are limited to GET /api/v1/quotes/by-code?short_code=... and the public /quote/{short_code} viewer URL returned in share_url.
| 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://api.salesbooth.com/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 |
Public quote viewer: Customers open the generated share_url in a browser at /quote/{short_code}. For JSON-based customer experiences, use the unauthenticated /api/v1/quotes/by-code?short_code={short_code} alias. Seller integrations should continue using the authenticated /api/v1/quotes endpoints for listing and retrieval.
deals:write scope. Returns 404 if the quote is not found or does not belong to the current workspace.| Parameter | Type | Description |
|---|---|---|
| id required | string | Quote ID to delete |
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": [
{
"type": "quote_sent",
"channel": "email",
"sent_at": "2026-03-18T10:00:00Z"
}
]
}
}| 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://api.salesbooth.com/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 scopes: products:read for POST simulations and comparisons; deals:read for GET negotiation-history analytics.
POST simulation and comparison pricing payloads include "simulation_only": true on each priced result to confirm no data was written. GET negotiation-history analytics returns aggregate analytics and does not include that flag.
| 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 |
| auto_discounts | array | Optional auto-discount definitions: { type?, value, description?, min_subtotal? }. Supports up to 20 entries for widget-style discount simulation. |
curl -X POST https://api.salesbooth.com/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",
"auto_discounts": [
{
"type": "percentage",
"value": 10,
"description": "Widget 10% off carts over $300",
"min_subtotal": 300
}
]
}'{
"error": false,
"success": true,
"data": {
"items": [
{
"product_id": "prod_aaa",
"quantity": 2,
"line_subtotal": 300
},
{
"product_id": "prod_bbb",
"quantity": 1,
"line_subtotal": 150
}
],
"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 |
Required filter: Provide either product_id or category. Requests with only period_days are rejected with 400 Either product_id or category is required.
| Parameter | Type | Description |
|---|---|---|
| product_id | string | Analyse patterns for a specific product. Required when category is omitted. |
| category | string | Analyse patterns for a product category. Required when product_id is omitted. |
| period_days | integer | Lookback window in days (1–365, default: 90) |
curl "https://api.salesbooth.com/v1/simulate-pricing?product_id=prod_123&period_days=90" \
-H "Authorization: Bearer sb_test_example_key_do_not_use"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: 5 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://api.salesbooth.com/v1/events/stream?events=deal.updated,deal.closed" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Accept: text/event-stream"// Note: the browser EventSource API does not support custom headers.
// Authenticate via the api_key query parameter instead.
const es = new EventSource(
'/api/v1/events/stream?events=deal.updated&api_key=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":"in_progress","_timestamp":"2026-03-15T10:30:00Z","_signature":"sha256=..."}
: heartbeat 2026-03-15T10:30:15+00:00
event: reconnect
data: {"reason":"timeout","last_event_id":"seq:1234"}Heartbeats are SSE comment lines (lines beginning with :). They keep the connection alive and reset proxy timeouts but are not dispatched as events by EventSource — no addEventListener('heartbeat', …) listener is needed.
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. Requires a publishable key (sb_pub_*) via Authorization: Bearer, X-API-Key, or the query parameter shown.
| Parameter | Type | Description |
|---|---|---|
| id | string | Retrieve a specific test with full stats |
curl https://api.salesbooth.com/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://api.salesbooth.com/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 |
variant: null and echoes the original publishable key. Public endpoint — uses a publishable key only.{
"error": false,
"success": true,
"data": {
"ab_test_id": "abt_xxxxx",
"api_key_a": "sb_pub_variant_a",
"api_key_b": "sb_pub_variant_b",
"weight_b": 50,
"widget_a_id": "wgt_variant_a",
"widget_b_id": "wgt_variant_b"
}
}{
"error": false,
"success": true,
"data": {
"variant": null,
"api_key": "sb_pub_xxxxx"
}
}Widget Public Endpoints
These 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, including holding and confirming booking slots for bookable products.
Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_....| Parameter | Type | Description |
|---|---|---|
| api_key | query string | Optional publishable key for callers that cannot send custom headers. Prefer Authorization: Bearer sb_pub_... or X-API-Key: sb_pub_... when possible. |
| products | string | Comma-separated product IDs to analyse (falls back to widget config) |
curl "https://api.salesbooth.com/v1/widget-intelligence?products=prod_aaa,prod_bbb" \
-H "Authorization: Bearer sb_pub_xxxxx"{
"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 | string | Publishable key (sb_pub_*). Required for authentication; may be sent in this body field, Authorization: Bearer, or X-API-Key. |
| 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 | string | Full legal name of the signer. Required when signature_mode is type. |
| signer_email | string | Optional signer’s email address |
| signature_mode | string | checkbox, type, draw, or digital |
| signature_image | string | Base64 PNG data URI for drawn signatures |
Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_....| Field | Type | Description |
|---|---|---|
| api_key | query string | Optional publishable key for browser callers that cannot send custom headers |
| code required | string | Promo code entered by the buyer |
| subtotal required | number | Cart subtotal to calculate the discount amount against |
| currency | string | Optional ISO 4217 effective checkout currency override. Send the buyer/widget currency for multi-currency sessions so fixed-amount promo validation and rounding use the active checkout currency. |
curl -X POST https://api.salesbooth.com/v1/widget-discount \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "code": "SAVE20", "subtotal": 450.00, "currency": "USD" }'{
"error": false,
"success": true,
"data": {
"valid": true,
"discount": {
"type": "percentage",
"value": 20,
"code": "SAVE20",
"description": "20% off your first purchase",
"amount": 90.00,
"min_subtotal": 0
}
}
}Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_.... The response returns sanitized rounds only, so embedded widget callers can read negotiation state without exposing private seller identifiers.| Field | Type | Description |
|---|---|---|
| deal_id required | query string | Widget-owned deal ID to load negotiation history for |
| api_key | query string | Optional publishable key for browser callers that cannot send custom headers |
curl "https://api.salesbooth.com/v1/widget-negotiations?deal_id=deal_xxxxx&api_key=sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"deal_id": "deal_xxxxx",
"rounds": [
{
"negotiation_id": "nego_xxxxx",
"deal_id": "deal_xxxxx",
"round_number": 1,
"proposer_type": "agent",
"proposed_terms": {
"pricing_model": "fixed",
"payment_terms": "due_on_receipt",
"custom_terms": {
"proposed_total": 900,
"original_total": 1000,
"currency": "USD"
}
},
"message": "Buyer proposal",
"status": "proposed",
"expires_at": "2026-03-20T18:00:00Z",
"created_at": "2026-03-18T11:24:00Z"
}
],
"total_rounds": 1
}
}Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_.... Use propose and counter with structured proposed_terms, accept to accept the latest seller round, or reject to decline it with an optional reason.| Field | Type | Description |
|---|---|---|
| deal_id required | query string | Widget-owned deal ID to mutate |
| action required | string | Must be propose, counter, accept, or reject |
| proposed_terms | object | Required for propose and counter. Structured deal terms such as pricing model, payment terms, and custom totals to negotiate. |
| message | string | Optional buyer message for propose or counter |
| expires_at | string | Optional ISO 8601 expiry timestamp for propose or counter |
| reason | string | Optional buyer-provided rejection reason for reject |
curl -X POST "https://api.salesbooth.com/v1/widget-negotiations?deal_id=deal_xxxxx&action=propose" \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"proposed_terms": {
"pricing_model": "fixed",
"payment_terms": "due_on_receipt",
"custom_terms": {
"proposed_total": 900,
"original_total": 1000,
"currency": "USD"
}
},
"message": "Could you do 900 if we sign today?",
"expires_at": "2026-03-20T18:00:00Z"
}'{
"proposed_terms": {
"pricing_model": "fixed",
"payment_terms": "due_on_receipt",
"custom_terms": {
"proposed_total": 950,
"original_total": 1000,
"currency": "USD"
}
},
"message": "I can agree at 950.",
"expires_at": "2026-03-21T18:00:00Z"
}{}{
"reason": "This is outside our budget for this quarter."
}{
"error": false,
"success": true,
"data": {
"negotiation_id": "nego_xxxxx",
"deal_id": "deal_xxxxx",
"round_number": 2,
"proposer_type": "agent",
"proposed_terms": {
"pricing_model": "fixed",
"payment_terms": "due_on_receipt",
"custom_terms": {
"proposed_total": 900,
"original_total": 1000,
"currency": "USD"
}
},
"message": "Could you do 900 if we sign today?",
"status": "proposed",
"expires_at": "2026-03-20T18:00:00Z",
"created_at": "2026-03-18T11:24:00Z"
}
}Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_.... The booking is created in held status and expires automatically if it is not confirmed.| Field | Type | Description |
|---|---|---|
| product_id required | string | Bookable product to reserve a slot for |
| staff_id required | string | Staff member assigned to the held slot |
| date required | string | Booking date in YYYY-MM-DD format |
| start_time required | string | Slot start time in HH:MM or HH:MM:SS format |
| duration | integer | Slot length in minutes. Defaults to 60; allowed range is 5 to 480 |
| customer_name | string | Buyer name attached to the held booking |
| customer_email | string | Buyer email address |
| customer_phone | string | Buyer phone number |
| notes | string | Optional booking notes stored with the hold |
curl -X POST https://api.salesbooth.com/v1/widget-bookings \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_service",
"staff_id": "stf_12345",
"date": "",
"start_time": "14:30",
"duration": 60,
"customer_name": "Alex Buyer",
"customer_email": "alex@example.com"
}' {
"error": false,
"success": true,
"data": {
"booking": {
"booking_id": "bkng_xxxxx",
"status": "held",
"product_id": "prod_service",
"staff_id": "stf_12345",
"booking_date": "",
"start_time": "14:30:00"
}
}
} Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_.... The response returns the updated booking in data.booking.| Field | Type | Description |
|---|---|---|
| id required | query string | Booking ID to confirm |
| action required | string | Must be confirm |
| deal_id required | string | Deal created from the held checkout session |
| line_item_id | string | Optional line item to associate with the confirmed booking |
| customer_id | string | Optional existing customer record to attach |
| confirmation_mode | string | auto (default) or manual |
curl -X PATCH "https://api.salesbooth.com/v1/widget-bookings?id=bkng_xxxxx" \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"action": "confirm",
"deal_id": "deal_xxxxx",
"line_item_id": "item_xxxxx",
"customer_id": "cust_xxxxx",
"confirmation_mode": "auto"
}'{
"error": false,
"success": true,
"data": {
"booking": {
"booking_id": "bkng_xxxxx",
"status": "confirmed",
"deal_id": "deal_xxxxx",
"customer_id": "cust_xxxxx"
}
}
}products:read. Authenticate with a publishable key passed as Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_....| Field | Type | Description |
|---|---|---|
| action required | string | Must be rules |
| product_id required | query string | Product ID to fetch compatibility and bundle rules for |
| api_key | query string | Optional publishable key for browser callers that cannot send custom headers |
curl "https://api.salesbooth.com/v1/widget-validate?action=rules&product_id=prod_aaa&api_key=sb_pub_xxxxx"{
"error": false,
"success": true,
"data": {
"product_id": "prod_aaa",
"compatibility_rules": [
{
"rule_id": "rule_123",
"source_option_id": "opt_pro",
"target_option_id": "opt_support",
"rule_type": "requires",
"message": "Pro requires Support",
"is_active": true
}
],
"bundle_rules": [
{
"bundle_id": "bundle_456",
"name": "Pro Bundle",
"discount_type": "fixed",
"discount_value": 25,
"min_options": 2,
"is_active": true,
"option_ids": ["opt_pro", "opt_support"]
}
]
}
}requires / excludes) and returns any price adjustments from includes_price rules. Use checkbox_values to scope checkbox-group required/min/max validation by option key when groups can share the same choice value. Call this before creating a deal to surface configuration errors early. Authenticate with a publishable key passed as Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_....| Field | Type | Description |
|---|---|---|
| api_key | query string | Optional publishable key for browser callers that cannot send custom headers |
| product_id required | string | Product to validate the configuration for |
| option_ids | string[] | Array of selected option IDs to validate against compatibility rules |
| text_values | object | Optional map of text option keys to buyer-entered values for required, max length, and pattern validation |
| checkbox_values | object | Optional map of checkbox-group option keys to selected choice values. Use this when checkbox groups can share the same choice value so required/min/max validation is scoped correctly per group. |
curl -X POST https://api.salesbooth.com/v1/widget-validate \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "product_id": "prod_aaa", "option_ids": ["opt_pro", "yes"], "text_values": { "engraving": "For Alex" }, "checkbox_values": { "support_options": ["yes"] } }'{
"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"
}
]
}
}Authorization: Bearer sb_pub_..., X-API-Key: sb_pub_..., or ?api_key=sb_pub_....| Field | Type | Description |
|---|---|---|
| api_key | query string | Optional publishable key for browser callers that cannot send custom headers |
| items | array | Multi-item cart to preview. Use this or the legacy product_id shape. Each item accepts product_id (required), optional option_ids, optional keyed option_selections, and optional quantity (default: 1) |
| product_id | string | Legacy single-item product ID. Required only when items is omitted |
| option_ids | string[] | Legacy single-item selected option IDs — their price modifiers are included in the subtotal |
| option_selections | object | Legacy single-item selected option values keyed by configuration option key. Use this with option_ids when inline option schema context matters |
| quantity | integer | Legacy single-item quantity (default: 1) |
| promo_code | string | Legacy single promo code to factor into the discount calculation |
| promo_codes | string[] | Promo codes to apply in order after bundle discounts |
| currency | string | Currency override for the preview. Defaults to the widget currency or USD |
| payment_type | string | Payment collection mode override: full, deposit, or quote |
| deposit_type | string | Deposit calculation method override: fixed or percentage |
| deposit_value | number | null | Deposit amount override. Uses a fixed amount or a percentage based on deposit_type |
curl -X POST https://api.salesbooth.com/v1/widget-payment-preview \
-H "Authorization: Bearer sb_pub_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"product_id": "prod_aaa",
"option_ids": ["opt_pro"],
"option_selections": { "plan": "pro" },
"quantity": 1
},
{ "product_id": "prod_bbb", "quantity": 2 }
],
"promo_codes": ["SAVE20", "BUNDLE10"],
"currency": "USD",
"payment_type": "deposit",
"deposit_type": "percentage",
"deposit_value": 25
}'{
"error": false,
"success": true,
"data": {
"items": [],
"subtotal": 450.00,
"discount": 90.00,
"bundle_discounts": [],
"bundle_discount_total": 0,
"promo_discount": 90.00,
"promo_details": {
"code": "SAVE20",
"discount_type": "percentage",
"discount_value": 20,
"discount_amount": 90.00
},
"auto_discount_total": 0,
"auto_discounts_applied": [],
"compatibility_errors": [],
"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://api.salesbooth.com/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://api.salesbooth.com/v1/schema/state-machine?entity=deals"{
"error": false,
"success": true,
"data": {
"schema_version": "1.0.0",
"entity_types": ["deals", "contracts"],
"deals": {
"entity_type": "deal",
"initial_state": "draft",
"states": {
"draft": {
"terminal": false,
"transitions": [
{
"target": "in_progress",
"conditions": [
{ "field": "line_items", "check": "not_empty", "description": "Deal must have at least one line item" }
],
"action": { "method": "POST", "endpoint": "/api/v1/deals?id={deal_id}&action=transition&status=in_progress" },
"required_scope": "deals:write"
}
]
},
"in_progress": { "terminal": false, "transitions": [{ "target": "pending_signature" }, { "target": "pending_payment" }] },
"pending_signature": { "terminal": false, "transitions": [{ "target": "pending_payment" }] },
"awaiting_signatures": { "terminal": false, "transitions": [{ "target": "pending_payment" }] },
"pending_payment": { "terminal": false, "transitions": [{ "target": "closed" }] },
"partially_accepted": { "terminal": false, "transitions": [{ "target": "pending_signature" }, { "target": "closed" }] },
"closed": { "terminal": true, "transitions": [] },
"cancelled": { "terminal": true, "transitions": [] },
"expired": { "terminal": true, "transitions": [] }
}
}
}
}curl "https://api.salesbooth.com/v1/schema/deal-terms"{
"error": false,
"success": true,
"data": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://salesbooth.com/api/schemas/deal-terms.json",
"title": "Deal Terms",
"type": "object",
"properties": {
"terms_schema_version": {
"type": "string",
"const": "1.0"
},
"payment_terms": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["upfront", "net_7", "net_14", "net_30", "net_60", "net_90", "milestone", "installment", "upon_delivery", "custom"]
},
"due_date": {
"type": "string",
"format": "date-time"
}
}
},
"delivery": {
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["digital", "physical", "service", "hybrid"]
},
"estimated_days": {
"type": "integer",
"minimum": 0
}
}
},
"warranty": {
"type": "object",
"properties": {
"duration_days": {
"type": "integer",
"minimum": 0
}
}
}
},
"additionalProperties": false
}
}Object Schemas
Human-readable field definitions for the core resource objects returned by the API.
Deal Object
{
"item_id": "item_xxxxx",
"deal_id": "deal_xxxxx",
"product_id": "prod_xxxxx",
"quantity": 2,
"unit_price": "499.00",
"subtotal": "998.00"
}Customer Object
Product Object
Health Check
Public endpoint for monitoring and infrastructure probes. No authentication required. Rate limited to 10 requests per minute per IP.
?type=readiness returns HTTP 503 under the same conditions, using the same flat health payload. Use ?type=liveness for an explicit always-200 upstream probe.| 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",
"timestamp": "2026-03-18T10:00:00Z",
"checks": {
"database": { "status": "healthy" },
"redis": { "status": "healthy" },
"cache": {
"status": "healthy",
"backends": {
"apcu": true,
"redis": true
}
},
"api": {
"status": "healthy",
"requests_5m": 42,
"error_rate_5m": 0,
"avg_response_ms": 118
},
"circuit_breakers": {
"status": "healthy",
"services": {
"stripe": "closed",
"twilio": "closed",
"email": "closed",
"mcp_tools": "closed",
"geolocation": "closed"
},
"details": {
"stripe": {
"state": "closed",
"failure_count": 0,
"consecutive_successes": 0,
"backoff_multiplier": 1,
"last_failure_at": null,
"opened_at": null,
"closed_at": "2026-03-18 09:58:00"
}
}
},
"job_queue": {
"status": "healthy",
"total_pending": 0,
"by_topic": {},
"redis_queue_depths": {},
"redis_total_pending": 0
},
"disk": {
"status": "healthy",
"used_percent": 41.2,
"free_mb": 15236
}
}
}?type=readiness){
"status": "unhealthy",
"timestamp": "2026-03-18T10:00:00Z",
"checks": {
"database": { "status": "unhealthy", "message": "Connection failed" },
"redis": { "status": "healthy" },
"cache": {
"status": "degraded",
"backends": {
"apcu": true
}
},
"api": {
"status": "degraded",
"requests_5m": 312,
"error_rate_5m": 18.27,
"avg_response_ms": 842
},
"circuit_breakers": {
"status": "degraded",
"services": {
"stripe": "open",
"twilio": "closed",
"email": "closed",
"mcp_tools": "half_open",
"geolocation": "closed"
},
"details": {
"stripe": {
"state": "open",
"failure_count": 5,
"consecutive_successes": 0,
"backoff_multiplier": 4,
"last_failure_at": "2026-03-18 09:59:12",
"opened_at": "2026-03-18 09:59:12",
"closed_at": null
}
}
},
"job_queue": {
"status": "degraded",
"total_pending": 12458,
"by_topic": {
"emails": { "pending": 12458, "processing": 2 }
},
"redis_queue_depths": {
"emails": 12458
},
"redis_total_pending": 12458
},
"disk": {
"status": "warning",
"used_percent": 96.1,
"free_mb": 412
}
}
}Deal Participants
Manage multi-party deal collaboration. Participants are agents or humans invited to a deal with specific roles, optional scope details, line-item assignments, and revenue-share allocations. Supported roles: primary, subcontractor, reviewer, observer, required_signer.
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_api_key_id required | integer | API key record ID for the agent being invited |
| role required | string | primary, subcontractor, reviewer, observer, or required_signer |
| revenue_share_percent | number | Revenue share percentage (0–100) |
| revenue_share_fixed | number | Fixed revenue-share amount in deal currency |
| scope_description | string | Optional description of the participant’s delegated scope |
| line_item_ids | string[] | Optional list of line item IDs assigned to this participant |
| delegation_id | string | Optional delegation record associated with this invitation |
curl -X POST "https://api.salesbooth.com/v1/deal-participants?deal_id=deal_abc" \
-H "Authorization: Bearer sb_test_example_key_do_not_use" \
-H "Content-Type: application/json" \
-d '{
"agent_api_key_id": 123,
"role": "subcontractor",
"revenue_share_percent": 25,
"scope_description": "May negotiate this deal",
"line_item_ids": ["item_abc"]
}'draft or in_progress status.Payment Intent
Create a payment intent 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 the payment form to complete payment on the client.This endpoint supports the optional Idempotency-Key header. Reuse the same key when retrying the same payment-intent creation request after a timeout or lost response.
| Field | Type | Description |
|---|---|---|
| deal_id required | string | Deal to create a PaymentIntent for |
| amount | number | Optional expected charge amount in the deal currency. If provided, it must match the server-authoritative remaining required deposit or remaining full deal total; mismatches return 409 pricing_mismatch. |
{
"error": false,
"success": true,
"data": {
"client_secret": "pi_xxx_secret_yyy",
"payment_intent_id": "pi_xxx",
"amount": 1499.00,
"currency": "usd"
}
}Changelog
The public changelog is pending publication. Until entries are backfilled, use the API, SDK, and widget version notes above as the source of truth for current developer-facing changes.
© 2026 Salesbooth. OpenAPI Spec