NAV

Introduction

Welcome to the Cord API!

This document describes integration APIs and flows available to partners who wish to embed the Cord experience directly into their application

Use the Cord API to pre-populate the identities of your teams and users. This ensures a fast and seamless user experience, where the Cord sidebar appears immediately and the user is already logged in. This architecture ensures that when a user is requesting a page, Cord already has all their details (name, profile picture) as well as the other people in their organization.

Get started with Cord

Authentication

1. Generate a signed app token:

import jwt from 'jsonwebtoken';

const signed_app_token = jwt.sign({ app_id: '<APP_ID>' }, '<SHARED_SECRET>', {
  expiresIn: '1 min',
  algorithm: 'HS512',
});

2. Exchange the signed app token for an access token:

curl "https://api.cord.com/v1/authorize" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{ "signed_app_token": "<SIGNED_APP_TOKEN>" }'

If successful, the response will be:

{
  "access_token": "eyJzZX...",
  "expires": "2021-06-29T14:54:17.050Z" // ISO timestamp
}

Each integration partner has an application ID and an associated shared secret, which can be obtained through the developer dashboard and stored securely on the server, never exposed client side.

All API requests must include a valid access token as in the request headers: Authorization: Bearer <ACCESS_TOKEN>.

To obtain an access token, the application ID and shared secret are combined to generate a signed app token, which is then exchanged for the access token using the /v1/authorize API. Access tokens have a limited lifespan (expiration is provided in the API response, normally 24 hours).

The signed app token is a JWT that must be generated server-side, with a short expiration (1 minute), containing the application ID in the payload app_id field, signed with the shared secret using the HS512 (HMAC using SHA-512 hash) algorithm.

HTTP Request

POST https://api.cord.com/v1/authorize

Request Body

Field Type Description
signed_app_token string required The signed app token generated as described above.

Users

Create a user

Example request that creates a user:

curl "https://api.cord.com/v1/users" \
  -X POST \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "3001",
    "name": "Philip J Fry",
    "email": "delivery@planetexpress.nny",
    "first_name": "Philip",
    "last_name": "Fry"
  }'

If successful, the response will be:

{
  "success": true
}

HTTP Request

POST https://api.cord.com/v1/users

Request Body

Field Type Description
id string required Partner-specific unique user ID
email string required Email address
name string optional Full user name
status string optional active OR deleted
profile_picture_url string optional Profile picture URL
first_name string optional First name
last_name string optional Last name

Create or update a user

Example request that updates a user's name and profile picture:

curl "https://api.cord.com/v1/users/123" \
  -X PUT \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Bender Bending Rodriguez",
    "profile_picture_url": "https://cord.com/favicon-32x32.png"
  }'

If successful, the response will be:

{
  "success": true
}

This endpoint creates or updates a user:

HTTP Request

PUT https://api.cord.com/v1/users/<ID>

Request Body

Field Type Description
email string required on create Email address
name string optional Full user name
status string optional active OR deleted
profile_picture_url string optional Profile picture URL
first_name string optional First name
last_name string optional Last name

Organizations

Create an organization

Example request to create an organization:

curl "https://api.cord.com/v1/organizations" \
  -X POST \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "10",
    "name": "Planet Express",
    "members": ["4", "42"]
  }'

If successful, the response will be:

{
  "success": true
}

HTTP Request

POST https://api.cord.com/v1/organizations

Request Body

Field Type Description
id string required Partner-specific unique organization ID
name string required Organization name
status string optional active OR deleted
members string[] optional List of partner-specific IDs of the users who are members of this organization

Create or update an organization

Example request to update and organization's status to deleted

curl "https://api.cord.com/v1/organizations/456" \
  -X PUT \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "deleted",
  }'

If successful, the response will be:

{
  "success": true
}

This endpoint creates or updates an organization:

HTTP Request

PUT https://api.cord.com/v1/organizations/<ID>

Request Body

Field Type Description
name string required on create Organization name
status string optional active OR deleted
members string[] optional List of partner-specific IDs of the users who are members of this organization

Batch

Example request that operates on several users and organizations:

curl "https://api.cord.com/v1/batch" \
  -X POST \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "organizations": [
      {
        "id": "10",
        "name": "Planet Express",
        "members": ["4", "42"]
      }
    ],
    "users": [
      {
        "id": "4",
        "name": "Hubert Farnsworth",
        "email": "hubert@planetexpress.nny"
      },
      {
        "id": "42",
        "name": "Leela Turanga",
        "email": "leela@planetexpress.nny"
      }
    ]
  }'

For cases where multiple actions need to be taken at once, we're also exposing a batch API endpoint that should reduce the need to orchestrate multiple API requests. The action taken for each entity is a "create or update" depending on whether the entity exists or not, based on its ID.

HTTP Request

POST https://api.cord.com/v1/batch

Request Body

Field Type Description
users user[] optional List of user objects. Every object must include the id field. If the user already exists, all other fields are optional and only updated when present. If the user does not already exist, fields are required as described above in the "Create a user" API.
organizations organization[] optional List of organization objects. Every object must include the id field. If the organization already exists, all other fields are optional and only updated when present. If the organization does not already exist, fields are required as described above in the "Create an organization" API.

Embedding Cord

1. Generate the user session token on the server side:

import jwt from 'jsonwebtoken';

const session_token = jwt.sign(
  {
    app_id: '<APP_ID>',
    user_id: '<USER_ID>',
    organization_id: '<ORGANIZATION_ID>',
  },
  '<SHARED_SECRET>',
  {
    expiresIn: '1 min',
    algorithm: 'HS512',
  },
);

2. Embed and initialize the Cord library on the client side:

<script src="https://app.cord.com/embed/latest.js"></script>
<script>
  window.cord.init({
    session_token: '<SESSION_TOKEN>',
  });
</script>

The Cord library can be embedded into the partner page via a <script> tag, after which it must be initialized with a session token.

<script src="https://app.cord.com/embed/latest.js"></script>

To initialize Cord on the page, call window.cord.init({ session_token: "..." }).

The session token is a JWT that:

To remove the Cord instance from the page, call window.cord.destroy().

Events

document.addEventListener('cord:sidebar.open', (e) => {
  console.log('cord sidebar is open, width: ' + e.detail.width);
});

Cord dispatches standard DOM events on the document element for various lifecycle stages or user actions.

For example, you might adjust your website layout based on changes in the sidebar, such as the sidebar opening, closing, or resizing.

Some events contain additional information in the event.detail property.

Event name Event detail Description
cord:sidebar.open { width: number } The sidebar is now open with a specific width.
cord:sidebar.close undefined The sidebar is now closed.
cord:sidebar.resize { width: number } The sidebar has been resized to adapt to browser width.

Custom Page Titles

<meta property="cord:title" content="Picnic Location - Poll Results" />

Inbox item with custom page title

By default, when showing the context of a conversation (in the inbox, email notifications, etc) we use the document.title of the page the conversation is happening on.

If you'd like to have custom, Cord-specific page titles, you can add a <meta> tag in the document <head>.

Improving annotations precision

<div data-cord-target="unique value" />

When users place an annotation on the page, Cord computes a selector which will be used to attach the pointer to the right element. This works well, but does not cover all scenarios.

To make annotations more robust on your pages, you can add a cord-target data attribute to the HTML tag that represents each individual element of interest.

Having a unique value will ensure the annotation will always point to the correct element.

Script Integrity Hash

To get the latest library version URL and integrity hash:

curl "https://api.cord.com/v1/embed"

Example response:

{
  "version": "1.42.0",
  "script_url": "https://app.cord.com/embed/6524bf41eac2493756a666ec692a64de.js",
  "script_integrity": "sha384-t/K96C2LsTqZG3dkFox5quuZfeOrvfQy4OcH6ZxzpzSSi+Msx5v7cPmuJ9cHjPGN"
}

Then, use these values when embedding the script tag:

<script
  src="https://app.cord.com/embed/6524bf41eac2493756a666ec692a64de.js"
  integrity="sha384-t/K96C2LsTqZG3dkFox5quuZfeOrvfQy4OcH6ZxzpzSSi+Msx5v7cPmuJ9cHjPGN"
  crossorigin="anonymous"
></script>

If you would like to have an integrity hash defined on the <script> tag, you can poll the embed API endpoint to obtain the most recent, version-specific URL for the library, along with its integrity value.

This API should ideally be polled daily. This ensures we can move quickly to deprecate old APIs and deliver new features and bug fixes.

HTTP Request

GET https://api.cord.com/v1/embed

Errors

Example request with missing access token:

curl "https://api.cord.com/v1/users/123" \
  -X PUT \
  -H "Content-Type: application/json" \
  -d '{
    "profile_picture_url": "https://cord.com/favicon-32x32.png"
  }'
{
  "error": "missing_authorization_header",
  "message": "Authorization header bearer token must be present."
}

Example request with an unexpected field:

curl "https://api.cord.com/v1/organizations/123" \
  -X PUT \
  -H "Content-Type: application/json" \
  -d '{ "foo": "bar" }'
{
  "error": "unexpected_field",
  "message": "foo is not a valid field name for this request. Expected 3 optional fields: name, status and members."
}

We do our best to return clear and detailed error messages for various error cases. The table below describes the types of errors that might be returned, along with a general description of what that type of error means. In practice, the error response will also contain a message field that offers more specific information about what went wrong.

HTTP Error Description
400 invalid_request The request body is not a valid JSON object or is empty.
400 unexpected_field A field is present in the request but is not one of the required or optional fields.
400 missing_field A required field is missing from the request.
400 invalid_field The value for a field is an incorrect type.
401 application_not_found The referenced application ID does not exist.
401 user_not_found The request references a user ID which has not yet been created.
401 organization_not_found The request references an organization ID which has not yet been created.
401 missing_authorization_header The API you're calling expects an Authorization header, which is missing.
401 invalid_authorization_header The Authorization header is either not a valid JWT or is missing the Bearer prefix.
401 invalid_app_token The app token not a valid JWT or is signed incorrectly.
401 invalid_access_token The access token is invalid. You should request another one.
401 expired_access_token The access token is expired or has been revoked. You should request another one.
409 organization_already_exists The organization you're trying to create already exists.
409 user_already_exists The user you're trying to create already exists.
500 error Generic internal server error.