Skip to main content
API Reference

Integrate Demodesk in your custom workflows for full flexibility and full control.

Marcel Seibold avatar
Written by Marcel Seibold
Updated over 2 weeks ago

Elevate the quality of your products and streamline your operational workflows by leveraging the seamless integration capabilities of the Demodesk API. With its holistic integration, Demodesk is designed to easily blend with your existing products and workflows, boosting your operational efficiency to new heights. Unleash the synergy of Demodesk and your products and redefine what is possible in operational excellence.


Highlights

  1. Effortless Scheduling and User Creation: With the Demodesk API, automating tasks such as scheduling meetings or creating user accounts becomes a breeze. While users continue to engage with the intuitive Demodesk dashboard, the API works silently in the background, streamlining operations and enhancing user experience.

  2. Complete White-Label Integration:

    • Your brand, front and center: With Demodesk's white-label solution, your brand stays in the spotlight. Our platform seamlessly merges with your digital ecosystem, ensuring that your customers interact solely with your branded experience.

    • Your Domain, Your Meeting Space: Deploy a full white-label solution on your domain (e.g., meet.yourdomain.com) and provide a personalized, branded meeting space for your customers, further enhancing trust and brand cohesion.

    • Simplified Logins: Say goodbye to the hassle of managing multiple logins. Our integration ensures a synchronized login experience with your host application, providing a streamlined access point for users.

    • Your custom Dashboard: Keep the controls and build your own dashboard in your application. Our API will handle all the interactions in the background.

API Reference

Company and User Management

Create account

To create an account, follow these steps:

  1. Create account

  2. Provide company details

  3. Add users to the company

curl -X "POST" "https://demodesk.com/api/v1/auth" \
-H 'api-key: <MASTER_3RD_PARTY_API_KEY>' \
-H 'Content-Type: application/json' \
-d $'{
"confirmSuccessUrl": "confirmed",
"password": "passwordtologintodemodeskdashboard",
"firstName": "Stan",
"timeZone": "Europe/Berlin",
"email": "example@testco.com",
"promoCodeToken": "<PROMO_CODE>",
"lastName": "Test",
"locale: "en"
}'

Request:

  • Password: you can set a password here in case you want to manually login to the Demodesk dashboard.

  • PromoCodeToken: pass the code you received in the partnership agreement. This will activate the floating license functionality for the account, set the appropriate trial duration and exclude this company from any Demodesk marketing campaign.

Response:

  • apiKey: this API key is the master key for the company account COMPANY_ADMIN_USER_API_KEY

  • status: ok or error (see errors[0].details for explanation)

Create company

curl -X "POST" "https://demodesk.com/api/v1/companies" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY>' \
-H 'Content-Type: application/json' \
-d $'{
"data": {
"attributes": {
"name": "Test Co",
"countryCode": "DE",
"timeZone": "Europe/Berlin"
}
}
}'

Response:

  • id: id of newly created company

  • status: ok or error (see errors[0].details for explanation)

Get users of a company

curl "https://demodesk.com/api/v1/companies/<company_id>/users" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY>'

Add users to company

curl -X "POST" "https://demodesk.com/api/v1/users" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY>' \
-H 'Content-Type: application/json' \
-d $'{
"data": {
"type": "users",
"attributes": {
"firstName": "First",
"email": "first.last@testco.com",
"lastName": "Last",
"locale": "en"
}
}
}'

Response:

  • id: id of the newly created user

  • apiKey: user's API key. Use this one to request auth tokens from the Demodesk backend to login this specific user.

  • status: ok or error (see errors[0].details for explanation)

Update user data

curl -X "PATCH" "https://demodesk.com/api/v1/users/<user_id>" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>' \
-H 'Content-Type: application/json' \
-d $'{
"data": {
"id": "<user_id>",
"type": "users",
"attributes": {
"firstName": "First",
"email": "first.last@testco.com",
"lastName": "Last"
}
}
}'

Response:

  • status: ok or error (see errors[0].details for explanation)

Delete user

curl -X "DELETE" "https://demodesk.com/api/v1/users/<user_id>" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>'

Response:

  • status: ok or error (see errors[0].details for explanation)

Meeting management

Create meetings

curl -X "POST" "https://demodesk.com/api/v1/scheduled_demos" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>' \
-H 'Content-Type: application/json' \
-d $'{
"data": {
"type": "demos",
"attributes": {
"account": "Test Account" # Meeting Name,
"timeZone": "Berlin",
"urls": [
"https://google.com",
"https://dict.cc"
],
"startDate": "2020-05-25T11:30:00.000+01:00",
"countryCode": "US",
"duration": 1800,
"user_id": "<user_id>",
"setup_phone": "full",
}
}
}'

Response:

  • id: meeting id

  • status: ok or error (see errors[0].details for explanation)

Get meeting details

curl "https://demodesk.com/api/v1/scheduled_demos/<demo_token>"

Response:

  • id: meeting id

  • attributes.status: one of scheduled, starting, started, ending, ended, canceled

  • status: ok or error (see errors[0].details for explanation)

Example response:

{
"data": {
"id": "<demo_id>",
"type": "demos",
"attributes": {
"status": "ended",
"duration": 3600,
"locale": "nl",
"countryCode": "NL",
"timeZone": "Europe/Amsterdam",
"account": "Example account",
"setupPhone": "full",
"twilioPhone": null,
"token": "PYBGFKMZ",
"createdAt": "2020-08-25T14:25:49.579Z",
"updatedAt": "2020-09-02T12:56:02.451Z",
"startDate": "2020-09-02T11:00:00.000+02:00",
"link": "https://demodesk.com/XXXXXXXX",
"controlUrl": "https://aws-eu-central-1.demodesk.com/api/v1/renderers/50dab44f-ecdb-4b12-8831-b0fccd608e26",
},
"relationships": {
"user": {
"data": {
"id": "<user_id>",
"type": "users"
}
},
"participants": {
"data": [
{
"id": "<participant_id>",
"type": "participants"
}
]
},
}
},
"included": [
{
"id": "<participant_id>",
"type": "participants",
"attributes": {
"kind": "browser",
"firstName": "Evi",
"lastName": null,
"fullName": "Evi",
},
},
],
"status": "ok"
}

List meetings

curl -X "GET" "https://demodesk.com/api/v1/demos?page=1&filter[schedule_eq]=upcoming&filter[all_team_members_dashboard]=true" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>' \
-H 'Content-Type: application/json'

Parameters (optional):

More on search matchers for filters are documented here.

  • filter[schedule_eq] can be past or upcoming, ie. show only past or upcoming meetings

  • filter[all_team_members_dashboard] can be true or false, ie. whether to show meetings only for the API key user or for the full team

  • filter[account_i_cont] string that filters for the meeting name

  • filter[start_date_gteq] meeting happened after date, e.g. 2022-10-10

  • filter[start_date_lteq] meeting happened before date, e.g. 2022-10-13

  • filter[group_id_eq] filters for meetings from group. Needs the group id.

  • filter[recordings_present] can be true or false, ie. whether the meeting was/is going to be recorded

  • filter[template_name_i_cont] filters for event type name

  • page is the requested page number, starting with 1. This endpoint is paginated, the total number of pages can be found in response→meta→totalPages

Response:

  • data: array of matching meetings

    • id: meeting id

    • attributes.status: one of scheduled, starting, started, ending, ended, canceled

  • status: ok or error (see errors[0].details for explanation)

  • meta: pagination information

Example response:

{
"data": [
{
"id": "123",
"type": "demos",
"attributes": {
"status": "started",
"duration": 1800,
"account": "Instant meeting",
"link": "https://demodesk.com/ABCDEFGH",
"token": "ABCDEFGH",
"userFirstName": "John",
"userLastName": "Doe",
"startDate": "2022-10-17T15:55:37.286+01:00",
},
"relationships": {
"user": {"data": {"id": "9773","type": "users"}},
"booker": {"data": {"id": "9773","type": "users"}},
"participants": {"data": [{"id": "3427341","type": "participants"}]},
"demoType": {"data": {"id": "5842","type": "demoTypes"}}, // playbook
"demoTemplate": {"data": {"id": "7194","type": "demoTemplates"}}, // event type
"recordings": {"data": []},
"scheduledShadowUsers": {"data": []} // invited shadows
}
},
{
// next demo
}
],
"included": [
// includes all objects that are references in "relationships"
],
"status": "ok",
"meta": {
"totalCount": 668,
"pageSize": 25,
"totalPages": 27,
"currentPage": 1
}
}

Update meeting

curl -X "PATCH" "https://demodesk.com/api/v1/scheduled_demos/<demo_id>" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>' \
-H 'Content-Type: application/json' \
-d $'{
"data": {
"type": "demos",
"id": "<demo_id>",
"attributes": {
"account": "Updated account name"
}
}
}'

Response:

  • id: meeting id

  • status: ok or error (see errors[0].details for explanation)

Cancel meeting

curl -X "POST" "https://demodesk.com/api/v1/scheduled_demos/<demo_id>/cancel" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>'

Response:

  • id: meeting id

  • status: ok or error (see errors[0].details for explanation)

Delete meeting

curl -X "DELETE" "https://demodesk.com/api/v1/scheduled_demos/<demo_id>" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>'

Response:

  • status: ok or error (see errors[0].details for explanation)

Recordings

Access recording transcript

curl "https://demodesk.com/api/v1/recordings/<RECORDING_TOKEN>/transcript" \
-H 'api-key: <COMPANY_ADMIN_USER_API_KEY> or <USER_API_KEY>' \
-H 'Accept: text/plain'

Error codes

Error responses are formatted in the following way:

{
"errors": [
{
"detail": "Error 1"
},
{
"detail": "Error 2"
}
],
"status": "not_found"
}

These errors are commonly used by all endpoints:

  • 404, not_found: The requested resource was not found

  • 422, unprocessable_entity: The sent update request was invalid. Check body for details.

  • 403, forbidden: Error: 'You are not authorized to perform this action.' You don't have the required authorization to perform the requested action

  • 401, unauthorized: Error: 'The provided API key is invalid.'

  • 403, forbidden: Error: 'Illegal association id update.'

  • 400, bad_request: Malformed request. Check body for detailed error message.

Webhook API

Please email alex@demodesk.com with the endpoints you want to be called for each event.

It’s possible to listen to the following webhook events from Demodesk:

'demo.scheduled'
'demo.rescheduled'
'demo.handovered'
'demo.canceled'
'demo.started'
'demo.ended'
'recording.uploaded'
'recording.transcription_postprocessed'

Example response:

{
"meta": {
"event": {
"action": "demo.started",
"apiVersion": "v1",
"createdAt": "2022-03-18T14:56:29.022Z"
}
},
"data": {
"id": "1234",
"type": "demos",
"attributes": {
"token": "ABCDEFGH",
"link": "https://demodesk.com/ABCDEFGH",
"location": "https://demodesk.com/meet/alexander-popp",
"name": "Meeting name",
"createdVia": "calendar_backsync",
"createdAt": "2022-03-14T17:55:33.000Z",
"updatedAt": "2022-03-18T14:56:26.000Z",
"hostEmail": "alex@demodesk.com",
"playbookName": "Playbook name",
"eventTypeName": "Event type name",
"tokens": {
"demoNotes": "Great lead. Should follow up next month",
"companySizeCustomToken": "100 - 200"
}
}
}
}

Terminology

  • 3rd Party Backend: Your server, used for fetching authentication tokens from the Demodesk backend.

  • 3rd Party Frontend: Your frontend code that implements the DemodeskAuth library.

  • Demodesk Backend: Demodesk’s backend, controls authentication and issues auth tokens.

  • Demodesk Frontend: Demodesk’s frontend, hosts the meeting dashboard.

Authentication

Authentication is managed through three types of API keys:

  • MASTER_3RD_PARTY_API_KEY: Your secret partnership API key. Use this to create new accounts.

  • COMPANY_ADMIN_USER_API_KEY: The master API key for each company. Administer individual company accounts with this key.

  • USER_API_KEY: Individual user API key. Administer individual user accounts with this key.

Login Flow

  1. DemodeskAuth library sends a request to the 3rd party backend to request an auth token.

  2. 3rd party backend forwards the request to Demodesk backend.

  3. Demodesk backend responds with an auth token.

  4. DemodeskAuth library writes authorization data to browser storage.

Implementation

DemodeskAuth JS

Get the demodesk-auth.js file from the Appendix and include DemodeskAuth in the 3rd party frontend and initialize it:

var auth = new DemodeskAuth({
foreignHost: 'yourdomain.com',
meetingHost: 'meet.yourdomain.com',
fetchAuthToken: function() {
return fetch('https://yourdomain.com/api/demodesk_auth') // this is the auth forward endpoint
.then(response => response.json())
.then(json => json.authToken);
},
});

Auth forward endpoint

In the 3rd party backend, create an endpoint that accepts and responds in the following format:

curl -X "POST" "https://demodesk.com/api/v1/users/login" \
-H 'api-key: <USER_API_KEY>'

The response should have a structure like this:

{
"status": "ok",
"meta": {
"access_token": "eyJhY2Nlc3NfdG9rZW4iOiIyTUx0RV9xUEdLblRkRHlVMFpBQndRIiwiY2xpZW50IjoidkFSVzRKc2VXYnM2a0VkZTV5V3RuZyIsImV4cGlyeSI6MTU5MzI1NTU0MiwidG9rZW5fdHlwZSI6IkJlYXJlciIsInVpZCI6ImFsZXhAZGVtb2Rlc2suY29tIiwicmVxdWVzdGVkX2F0IjoiMjAyMC0wNi0xM1QxMDo1OTowMloifQ=="
}
}

Appendix

demodesk-auth.js

DemodeskAuth = (function(){
var C = function(){ return constructor.apply(this,arguments); }

const AUTH_TOKEN_CACHE_KEY = 'demodesk.authTokenCache';
const MIN_TOKEN_EXPIRY_GRACE_DURATION = 7 * 24 * 60 * 60; // 7 days (max validity is 14 days)

var p = C.prototype;
p.foreignHost; // 'yourdomain.com';
p.meetingHost; // 'meet.yourdomain.com';
p.fetchAuthToken = () => void 0; // Function to fetch auth token from 3rd party backend
p.onAuthenticated = () => void 0; // Callback function to signal successful authentication

function constructor(options) {
this.foreignHost = options.foreignHost;
this.meetingHost = options.meetingHost;
this.fetchAuthToken = options.fetchAuthToken;
this.onAuthenticated = options.onAuthenticated;
}

// External functions

p.authenticate = function() {
if (isLastAuthTokenValid()) {
console.log('Last auth token is still valid. Doing nothing.');
} else {
console.log('Last auth token is expired. Fetching fresh one.');
writeFreshAuthToken(this);
}
}

p.logout = function() {
removeCachedAuthToken();
removeDemodeskCookies(this);
}

// Internal functions

async function writeFreshAuthToken(context) {
const token = await context.fetchAuthToken();

const bearerToken = parseAuthToken(token);

writeDemodeskCookies(bearerToken, context);

setCachedAuthToken(token);
}

function writeDemodeskCookies(bearerToken, context) {
var frameStyle = {
display: 'none',
position: 'absolute',
top: '-999px',
left: '-999px'
};

frame = window.document.createElement('iframe');
frame.id = 'demodesk-auth-iframe';
for (key in frameStyle) {
frame.style[key] = frameStyle[key];
}

frame.onload = function() {
setTimeout(() => {
context.onAuthenticated();
frame.remove();
}, 200);
};
window.document.body.appendChild(frame);

frame.src = "https://" + context.meetingHost + "/manage/static-user/remote-login.html?" +
"access_token=" + bearerToken['access_token'] +
"&client=" + bearerToken['client'] +
"&expiry=" + bearerToken['expiry'] +
"&token_type=" + bearerToken['token_type'] +
"&uid=" + encodeURIComponent(bearerToken['uid']) +
"&requested_at=2030-06-15T07:42:37Z" +
"&viewer_access=true";
}

function removeDemodeskCookies(context) {
var frameStyle = {
display: 'none',
position: 'absolute',
top: '-999px',
left: '-999px'
};

frame = window.document.createElement('iframe');
frame.id = 'demodesk-auth-iframe';
for (key in frameStyle) {
frame.style[key] = frameStyle[key];
}

frame.onload = function() {
setTimeout(() => {
frame.remove();
}, 200);
};
window.document.body.appendChild(frame);
frame.src = "https://" + context.meetingHost + "/manage/static-user/remote-logout.html"
}

function isLastAuthTokenValid() {
const currentTime = Math.floor(Date.now() / 1000);
console.log(`Current time: ${currentTime}`);
const tokenTime = getLastAuthTokenExpiry();
console.log(`Token time: ${tokenTime}`);

const remainingTokenDuration = tokenTime - currentTime;
console.log(`Remaining token duration: ${remainingTokenDuration}`);

return remainingTokenDuration >= MIN_TOKEN_EXPIRY_GRACE_DURATION;
}

function getLastAuthTokenExpiry() {
const cachedToken = getCachedAuthToken();
const parsedToken = parseAuthToken(cachedToken);
return parsedToken['expiry'];
}

function getCachedAuthToken() {
return localStorage.getItem(AUTH_TOKEN_CACHE_KEY);
}

function setCachedAuthToken(authToken) {
localStorage.setItem(AUTH_TOKEN_CACHE_KEY, authToken);
console.log('New auth token stored.');
}

function removeCachedAuthToken() {
localStorage.removeItem(AUTH_TOKEN_CACHE_KEY);
}

function parseAuthToken(authToken) {
const decodedString = atob(authToken);
return JSON.parse(decodedString);
}

return C;
})();

FAQs

Why do we receive multiple demoEnded webhooks with different demoStatus values for the same demo?

This can occur when a meeting is manually ended and then restarted. When the meeting is first ended, a demoEnded webhook is triggered with a specific demoStatus (e.g., "no-show"). If the meeting is restarted, another demoEnded webhook is sent with a different demoStatus, reflecting the updated state of the demo. This behavior is expected when a meeting is manually stopped and restarted.

Can a demo be canceled after it has started?

No, once a demo has started, it cannot be canceled via the API. If a customer cancels their appointment shortly before the scheduled start time, but the demo has already been triggered (e.g., via the demo.started webhook), it is no longer possible to cancel the demo. In such cases, the host's calendar remains blocked and the appointment must be removed manually. This behavior is intended to prevent issues with state handling, such as shutting down a demo that hasn't fully started or when the renderer has not yet been assigned.

Did this answer your question?