Developers · Included in Business
DemoSix for developers and agents
Read who watched, how far, and the leads your signature links bring in, from your own tools and AI assistants. Included in Business.
Step one
Get a key
A key is made by the workspace Owner and reaches only the Networks chosen when it was made.
- 1
Open Settings → Developers
Choose New key. Only the Owner of the workspace sees the controls.
- 2
Name it and pick its Networks
A key reaches the Networks you pick and no others. Pick the scopes it needs.
- 3
Copy it once
The key starts with dsx_ and is shown one time. Lose it, and you make a new one. Give it an expiry; 90 days is suggested.
Scopes
Keys read. The one exception is Manage webhooks, which adds and removes your own webhook addresses. Nothing a key does changes a lead, a demo or a Network.
leads:read- Read leads
watched:read- Read Who watched
demos:read- Read demos and totals
webhooks:manage- Manage webhooks
Send the key as a bearer token. The first call to make is /me: it answers with the workspace, the Networks and the scopes the key has.
curl -H "Authorization: Bearer dsx_..." \
https://demosix.com/api/v1/meRead it from your tools
The API
Every address begins with https://demosix.com/api/v1. The whole API is described in an OpenAPI 3.1 document at /api/v1/openapi.json, which needs no key to read. JSON in and out; dates are ISO 8601.
GET /me
Who this key is
GET /networks
The Networks the key reaches
GET /networks/{networkId}/leads
The Leads list
GET /networks/{networkId}/leads/find
One lead by email
GET /networks/{networkId}/leads/{leadId}
One lead
GET /networks/{networkId}/links
The Network's signature links
GET /networks/{networkId}/demo-requests
Demo requests
GET /networks/{networkId}/watched
Who watched, for one Channel
GET /networks/{networkId}/demos
Every demo with its totals
GET /demos/{demoId}/stats
One demo's totals
GET /webhooks
The workspace's webhook addresses
POST /webhooks
Add a webhook address
GET /webhooks/{endpointId}
One webhook
DELETE /webhooks/{endpointId}
Remove a webhook
POST /webhooks/{endpointId}/test
Send a sample event now
| Method | Path | What it answers |
|---|---|---|
| GET | /me | Who this key is |
| GET | /networks | The Networks the key reaches |
| GET | /networks/{networkId}/leads | The Leads list |
| GET | /networks/{networkId}/leads/find | One lead by email |
| GET | /networks/{networkId}/leads/{leadId} | One lead |
| GET | /networks/{networkId}/links | The Network's signature links |
| GET | /networks/{networkId}/demo-requests | Demo requests |
| GET | /networks/{networkId}/watched | Who watched, for one Channel |
| GET | /networks/{networkId}/demos | Every demo with its totals |
| GET | /demos/{demoId}/stats | One demo's totals |
| GET | /webhooks | The workspace's webhook addresses |
| POST | /webhooks | Add a webhook address |
| GET | /webhooks/{endpointId} | One webhook |
| DELETE | /webhooks/{endpointId} | Remove a webhook |
| POST | /webhooks/{endpointId}/test | Send a sample event now |
Pages
limit (up to 100) and cursor. Each page answers with next_cursor; send it back to get the next page, and stop when it is null.Limits
Retry-After header. Twenty refused tokens from one address in ten minutes close the door to that address for the rest of the window; a valid key is never counted.Errors
error object: a code, a message, sometimes details, and the requestId to quote to support.{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded",
"details": { "retryAfter": 12 },
"requestId": "req_01JA3V7Q8KXN2M4P6R8T0W2Y4Z"
}
}Error codes
Retry a 429, 500 or 503 after a growing wait. Any other error will answer the same way until the request or the key changes.
- 400
BAD_REQUEST - The body is not valid JSON.
- 401
UNAUTHORIZED - No key, or a key that is wrong, revoked or expired.
- 402
PAYMENT_REQUIRED - The workspace is not on Business. Tell a person; retrying will not help.
- 403
FORBIDDEN - The key does not have the scope this needs.
- 404
NOT_FOUND - It does not exist, or this key does not reach it.
- 409
CONFLICT - A limit stops it, such as the tenth key or webhook address, or a message that can no longer be sent again.
- 422
VALIDATION_ERROR - A parameter or field is wrong; the details say which.
- 429
RATE_LIMITED - Too many calls. Wait the seconds in Retry-After.
- 500
INTERNAL_ERROR - Something failed on our side. Try again shortly.
- 503
SERVICE_UNAVAILABLE - Briefly unavailable. Try again shortly.
Leads come back with the same filters the Leads page has: search, engagement, link, from, marketing and sort.
curl -H "Authorization: Bearer dsx_..." \
"https://demosix.com/api/v1/networks/NETWORK_ID/leads?engagement=ENGAGED&limit=50"One lead looks like this. A single lead's detail adds their answers, per-demo watching and consent history.
{
"id": "4f0c2a7e-6b1d-4c3e-9a21-0d5e8f7b6c11",
"network_id": "9b7e1d2c-3a4f-4e5d-8c6b-7a8f9e0d1c2b",
"first_name": "Maria",
"last_name": "Lopez",
"email": "maria@example.com",
"email_verified": true,
"email_verified_at": "2026-10-13T18:40:02Z",
"engagement_state": "ENGAGED",
"engaged_at": "2026-10-14T13:02:11Z",
"best_ratio": 0.62,
"demos_started": 2,
"first_submitted_at": "2026-10-13T18:40:02Z",
"last_activity_at": "2026-10-14T13:02:11Z",
"submission_count": 1,
"visit_count": 3,
"marketing_status": "GRANTED",
"calls_texts_agreed_at": null,
"link": { "id": "2c6d8e0f-1a2b-4c3d-8e9f-0a1b2c3d4e5f", "name": "Sales", "removed": false },
"link_owner": { "user_id": "user_31Kq8Zt", "name": "Alex Rivera", "handle": "alex" },
"url": "https://demosix.com/creator/networks/9b7e1d2c-3a4f-4e5d-8c6b-7a8f9e0d1c2b/leads?lead=4f0c2a7e-6b1d-4c3e-9a21-0d5e8f7b6c11"
}The moment it happens
Webhooks
Add an https address on Settings → Developers, pick the events and the Networks, and DemoSix posts a signed JSON message to it when one happens.
The events
lead.created- New lead
lead.engaged- Lead engaged (watched half a demo)
lead.demo_requested- Lead asked for a demo
lead.marketing_withdrawn- Lead's marketing permission withdrawn
lead.deleted- Lead deleted
A new lead carries email_verified: true when they entered the code emailed to them. There is no separate verified event, because where verification is on a lead exists only once they did.
The body names the event, the Network and the lead with every field the API shows, including their answers. Engaged messages add the demo; demo requests add the request text; withdrawals and deletions carry what you need to update your own copy.
{
"id": "dlv_01JA3V7Q8KXN2M4P6R8T0W2Y4Z",
"event_id": "evt_01JA3V7Q8KXN2M4P6R8T0W2Y4A",
"type": "lead.engaged",
"occurred_at": "2026-10-14T13:02:11Z",
"network": {
"id": "9b7e1d2c-3a4f-4e5d-8c6b-7a8f9e0d1c2b",
"name": "Northstar Software",
"web_name": "northstar"
},
"lead": {
"id": "4f0c2a7e-6b1d-4c3e-9a21-0d5e8f7b6c11",
"first_name": "Maria",
"last_name": "Lopez",
"email": "maria@example.com",
"email_verified": true,
"engagement_state": "ENGAGED",
"best_ratio": 0.62,
"answers": [{ "id": "q_1", "question": "Company size", "type": "SELECT", "answer": "11-50" }],
"url": "https://demosix.com/creator/networks/9b7e1d2c-3a4f-4e5d-8c6b-7a8f9e0d1c2b/leads?lead=4f0c2a7e-6b1d-4c3e-9a21-0d5e8f7b6c11"
},
"demo": { "id": "TepAqZEWbC", "title": "Pricing walkthrough", "ratio": 0.62 }
}Five headers come with it. X-Demosix-Event-Id is the same on every retry of the same event, so you can drop a duplicate by it.
POST /demosix-webhook HTTP/1.1
Content-Type: application/json
User-Agent: DemoSix-Webhooks/1
X-Demosix-Event: lead.engaged
X-Demosix-Event-Id: evt_01JA3V7Q8KXN2M4P6R8T0W2Y4A
X-Demosix-Delivery: dlv_01JA3V7Q8KXN2M4P6R8T0W2Y4Z
X-Demosix-Timestamp: 1792155731
X-Demosix-Signature: v1=5f1a9c0e7b2d4a6f8e1c3b5d7f9a0c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a5Check the signature
Each address has a secret that starts with whsec_, shown once when the address is added and again only when you rotate it. To check a message:
- Read the raw body as it arrived. Do not parse and re-serialize it first.
- Join
X-Demosix-Timestamp, a dot, and the raw body:${timestamp}.${rawBody}. - Compute HMAC-SHA256 over that string with the secret, as hex.
- Compare it to the value after
v1=inX-Demosix-Signature, with a constant-time compare. - Refuse a timestamp more than five minutes from your clock.
import { createHmac, timingSafeEqual } from "node:crypto";
// headers: the request's headers, lower-cased; rawBody: the body as a string
export function verifyDemoSix(secret, headers, rawBody) {
const timestamp = Number(headers["x-demosix-timestamp"]);
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const mac = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const expected = Buffer.from(`v1=${mac}`);
const given = Buffer.from(String(headers["x-demosix-signature"] ?? ""));
return given.length === expected.length && timingSafeEqual(given, expected);
}import hashlib, hmac, time
# headers: the request's headers; raw_body: the body as bytes
def verify_demosix(secret: str, headers: dict, raw_body: bytes) -> bool:
timestamp = int(headers["X-Demosix-Timestamp"])
if abs(time.time() - timestamp) > 300:
return False
signed = f"{timestamp}.".encode() + raw_body
expected = "v1=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(headers.get("X-Demosix-Signature", ""), expected)<?php
// $headers: the request's headers; $rawBody: file_get_contents('php://input')
function verifyDemoSix(string $secret, array $headers, string $rawBody): bool {
$timestamp = (int) ($headers['X-Demosix-Timestamp'] ?? 0);
if (abs(time() - $timestamp) > 300) {
return false;
}
$expected = 'v1=' . hash_hmac('sha256', "$timestamp.$rawBody", $secret);
return hash_equals($expected, $headers['X-Demosix-Signature'] ?? '');
}Retries
Pausing
Addresses
Order and repeats
X-Demosix-Event-Id. When order matters, read the lead from the API for its latest state.Where messages come from
Ask your AI
MCP
Connect your AI assistant to DemoSix and ask it who watched the pricing demo this week, or which leads from Sales are engaged but have not booked.
The MCP server is at https://demosix.com/api/mcp. It only reads: nothing it does changes anything. It needs a workspace on Business.
Claude
- Open Connectors (Customize or Settings) and choose Add custom connector.
- Name it DemoSix, paste
https://demosix.com/api/mcpand choose Add. - Choose Connect, sign in to DemoSix and choose Allow.
- In a chat, switch DemoSix on under Connectors in the + menu, and ask.
ChatGPT
- Open Plugins (Apps on some plans) and choose Add custom MCP server. In a workspace, an admin may have to allow it.
- Name it DemoSix, paste
https://demosix.com/api/mcp, keep OAuth and create it. - Continue to DemoSix, sign in and choose Allow.
- In a chat, type @DemoSix and your question.
Claude and ChatGPT sign in with your DemoSix account and read what you can read in the app. Other tools connect with an API key: send it as a bearer token in the Authorization header, the same way as the API, and it reads exactly what the key reaches.
{
"mcpServers": {
"demosix": {
"url": "https://demosix.com/api/mcp",
"headers": { "Authorization": "Bearer dsx_..." }
}
}
}Things to ask
- Which Networks do I have in DemoSix?
- Who watched the pricing demo this week, and how far did they get?
- Which leads from my Sales link are engaged but have not asked for a demo?
- Find the lead jane@acme.com and tell me what she watched.
- Give me this week's lead summary for my Network.
The tools
list_networkslist_leadsget_leadfind_lead_by_emaillist_demo_requestswho_watchedlist_demosget_demo_stats
Each tool is one API read, under the same scopes and limits as the key. Their descriptions tell the assistant what the figures mean: started against engaged, and what watched means on Who watched.
If it does not connect
- No sign-in page appears: remove the connector and add it again with the address exactly as above.
- It answers with no Networks: the workspace is not on Business, or the account that signed in owns or helps run no Network.
- It stopped answering: choose Connect on the connector again. Removing the connector, or revoking the key on Settings, Developers, ends its access.
- Still stuck: email support@demosix.com.
Build on it
Versions and changes
The API and webhooks are version 1, the /v1 in every address. We will not change version 1 in a way that breaks your code.
Can change in version 1
Needs version 2
The exception
Changes
October 16, 2026Key checksum
New keys end in a six-character checksum, so scanners can tell a real key from a look-alike. Same length and characters; older keys keep working.
October 15, 2026Paid Networks removed
A Network's type is now only PUBLIC or PRIVATE, and a demo request's from.kind is never MEMBER or GUEST. Requests are unchanged.
October 14, 2026Key safety
Keys can be given an expiry and revoked all at once, and the Owner is emailed when a key is made, revoked or first used from a new country.
October 14, 2026Version 1
API keys, the read API, webhooks, the MCP server for Claude, ChatGPT and other assistants, the Watch a Demo button and the in-page frame.
Good to know
Limits and rules
If a key leaks
If a key has been seen by anyone it should not have been, in public code, a log or a message, treat it as lost:
- Revoke it in DemoSix: Settings, then Developers. It stops working at once.
- Make a new key there.
- Put the new key in your integration's settings or secret store, not in code.
- Remove the old key from your code and its history.
- Check the key list for anything you do not recognise, and revoke it too.
- Keys, webhooks and the MCP server are part of Business. While a workspace is not on one of those plans, its keys answer 402, its webhooks pause and the MCP server refuses its Networks; everything works again the moment the plan is back.
- Key safety: the Owner is emailed when a key is made, when it is revoked, and the first time it is used from a new country, each time with a one-click revoke link. A key can be given an expiry (90 days suggested) and every key can be revoked at once. DemoSix has applied to GitHub's secret scanning program; once GitHub switches it on, a key found in public code on GitHub will be revoked for you as soon as GitHub reports it.
- The workspace Owner makes keys and webhook addresses. Anyone else sees the Developers settings page without its controls.
- A key reaches only the Networks chosen when it was made, with the scopes it was given. Up to 10 keys and 10 webhook addresses per workspace.
- Lead data is what the prospect gave the named company on its form, and the watching the consent wording told them that company receives. Use it as they were told.
- The withdrawn-permission and deleted-lead webhooks exist so your copies can follow the lead's decision. Subscribe to them if you keep leads anywhere else.
- Where data is stored, how it is encrypted and how long it is kept: the Security page.
- Questions and problems: support@demosix.com. Quote the
requestIdfrom an error if you have one.