Create Network Free

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. 1

    Open Settings → Developers

    Choose New key. Only the Owner of the workspace sees the controls.

  2. 2

    Name it and pick its Networks

    A key reaches the Networks you pick and no others. Pick the scopes it needs.

  3. 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
curl -H "Authorization: Bearer dsx_..." \
  https://demosix.com/api/v1/me

Read 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

Pages

Lists take 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

120 calls a minute per key. Who watched and the demo totals list are heavier: 10 a minute. Over the limit you get a 429 and a 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

Every error is JSON with one error object: a code, a message, sometimes details, and the requestId to quote to support.
An error
{
  "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.

400BAD_REQUEST
The body is not valid JSON.
401UNAUTHORIZED
No key, or a key that is wrong, revoked or expired.
402PAYMENT_REQUIRED
The workspace is not on Business. Tell a person; retrying will not help.
403FORBIDDEN
The key does not have the scope this needs.
404NOT_FOUND
It does not exist, or this key does not reach it.
409CONFLICT
A limit stops it, such as the tenth key or webhook address, or a message that can no longer be sent again.
422VALIDATION_ERROR
A parameter or field is wrong; the details say which.
429RATE_LIMITED
Too many calls. Wait the seconds in Retry-After.
500INTERNAL_ERROR
Something failed on our side. Try again shortly.
503SERVICE_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
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.

A lead
{
  "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.

A message
{
  "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.

Headers
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=5f1a9c0e7b2d4a6f8e1c3b5d7f9a0c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a5

Check 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:

  1. Read the raw body as it arrived. Do not parse and re-serialize it first.
  2. Join X-Demosix-Timestamp, a dot, and the raw body: ${timestamp}.${rawBody}.
  3. Compute HMAC-SHA256 over that string with the secret, as hex.
  4. Compare it to the value after v1= in X-Demosix-Signature, with a constant-time compare.
  5. Refuse a timestamp more than five minutes from your clock.
Node
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);
}
Python
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
<?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

Answer with any 2xx within ten seconds. Anything else is retried with growing waits for about a day, and each attempt is listed under Deliveries.

Pausing

After a day of failing, or 100 failures in a row, the address is paused and the Owner is emailed once. Resume, Redeliver and Send test are on Settings → Developers.

Addresses

Addresses must be https and reachable from the internet. Private and local addresses are refused, and redirects are not followed.

Order and repeats

Messages can arrive out of order: a retried message can land after a newer one. Each one is sent at least once, so drop a repeat by X-Demosix-Event-Id. When order matters, read the lead from the API for its latest state.

Where messages come from

From our hosting provider's servers in the United States. Their addresses change, so there is no fixed list to allow through a firewall. Trust a message by its signature, not by where it came 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

  1. Open Connectors (Customize or Settings) and choose Add custom connector.
  2. Name it DemoSix, paste https://demosix.com/api/mcp and choose Add.
  3. Choose Connect, sign in to DemoSix and choose Allow.
  4. In a chat, switch DemoSix on under Connectors in the + menu, and ask.

ChatGPT

  1. Open Plugins (Apps on some plans) and choose Add custom MCP server. In a workspace, an admin may have to allow it.
  2. Name it DemoSix, paste https://demosix.com/api/mcp, keep OAuth and create it.
  3. Continue to DemoSix, sign in and choose Allow.
  4. 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.

Cursor and Claude Code
{
  "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_networks
  • list_leads
  • get_lead
  • find_lead_by_email
  • list_demo_requests
  • who_watched
  • list_demos
  • get_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.

One tag

Watch a Demo on your website

Paste one tag where the button should go. It becomes a Watch a Demo button in the DemoSix gradient that opens your branded page in a new tab.

The tag is copied ready-made from the Share card beside Add to signature: Add to your website. It comes with your link's address filled in. The script sets no cookie, loads nothing else and sends nothing until the button is clicked.

The tag
<script async src="https://demosix.com/embed/v1.js" data-demosix-link="https://yourcompany.demosix.com/CODE" data-demosix-label="Watch a Demo" data-demosix-open="tab"></script>

For a site builder that strips scripts, use the same button as a picture inside a link. It is the moving button from the email signature.

Without a script
<a href="https://yourcompany.demosix.com/CODE" target="_blank" rel="noopener"><img src="https://demosix.com/api/share-links/CODE/signature-button?v=1&amp;w=Watch%20a%20Demo" alt="Watch a Demo" width="144" height="40" style="border:0"></a>

Content-Security-Policy

If your site has a Content-Security-Policy, allow script-src https://demosix.com.

Inside your own page

To show the form and the demos inside your website rather than in a new tab, list your site in Network Settings, Website embed, and paste the frame it gives you. A site you have not listed cannot frame your page. The frame sizes itself: the page inside it posts its height in a message of type demosix:height, and the short script after the frame sets the frame's height from it, taking the message only from its own frame. Visitors on a Safari older than 26.2 are asked for the short form again on their next visit inside the frame; the same email updates the same lead.

The frame
<iframe src="https://yourcompany.demosix.com/CODE"
        title="Watch a Demo"
        allow="autoplay; fullscreen; picture-in-picture"
        style="width:100%;min-height:480px;border:0"></iframe>
<script>
(function () {
  var frame = document.currentScript && document.currentScript.previousElementSibling;
  if (!frame || frame.tagName !== "IFRAME") return;
  window.addEventListener("message", function (event) {
    if (!frame.contentWindow || event.source !== frame.contentWindow) return;
    if (!event.data || event.data.type !== "demosix:height") return;
    var height = Number(event.data.height);
    if (height > 0) frame.style.height = Math.ceil(height) + "px";
  });
})();
</script>

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

New fields in an answer or a message, new endpoints, new optional parameters and new events. Ignore fields you do not know. A webhook address only gets the events it was set up for.

Needs version 2

Removing or renaming a field, changing what one means, or removing an endpoint or event. Version 1 keeps working for at least six months after we announce version 2, here and by email to every workspace Owner with a key or webhook address.

The exception

A change needed to fix a security problem may come without notice. It is listed below when it does. The MCP server's tools can gain tools and clearer wording at any time; assistants read them each time they connect.

Changes

  1. 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.

  2. 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.

  3. 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.

  4. 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:

  1. Revoke it in DemoSix: Settings, then Developers. It stops working at once.
  2. Make a new key there.
  3. Put the new key in your integration's settings or secret store, not in code.
  4. Remove the old key from your code and its history.
  5. 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 requestId from an error if you have one.