Authentication

Every request to https://api-v2.dropcowboy.com needs a credential. Use an API key from your own server; you create these yourself. AI tools connect with an OAuth token from a user's sign-in. Browser widgets use short-lived site tokens, which your server mints with its own credential.

Credential Send it as Use it for
API key x-key and x-secret headers Server-to-server calls. Create one in the dashboard or with the API.
OAuth token, user sign-in Authorization: Bearer <access_token> AI tools connected through our MCP server, acting as the signed-in user
OAuth token, client credentials Authorization: Bearer <access_token> A machine client set up by support
Site token Passed to the widget Building Blocks widgets in a browser

If a request carries both an API key and a bearer token, the API key is used.

Keep keys and secrets on your server. Never put them in browser code, a mobile app or a public repository.

API keys

An API key is a pair: the key, sent as x-key, and its secret, sent as x-secret. Create one in the dashboard under Developers > API keys, or with the API below. The secret is shown once, when the key is created.

Store both in environment variables and send them on every request:

export DC_KEY="7f3c9a1e-5b2d-4e8f-a6c4-9d1b3e7f5a20"
export DC_SECRET="c8e2a4f6-1d3b-4a9c-8e7f-2b5d9a1c6e34"

curl https://api-v2.dropcowboy.com/register/public/account \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"

Manage keys with the API

Method Route Scope What it does
GET /register/public/apikeys balance:read List your keys
POST /register/public/apikeys account:write Create a key
DELETE /register/public/apikeys/{key_id} account:write Delete a key

List keys

GET /register/public/apikeys returns every key on your account, without secrets.

curl https://api-v2.dropcowboy.com/register/public/apikeys \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
{
  "data": [
    {
      "_id": "3b8e1f6a-9c4d-4a2e-b7f5-1d6c8a3e9b47",
      "name": "Order notifications",
      "key": "7f3c9a1e-5b2d-4e8f-a6c4-9d1b3e7f5a20",
      "type": "standard",
      "team_id": "4e9a2c7f-1b5d-4f3e-8c6a-9d2b7e4f1a53",
      "scopes": ["contacts:read", "sms:send"],
      "mcp_client": null,
      "created_at": 1774041600000,
      "expires_at": null,
      "last_used_at": 1774128000000,
      "request_count": 1842
    }
  ],
  "meta": { "request_id": "c2a6e8f4-3b9d-4c1e-8a7f-5d3b1e9c6a24" }
}
Field Type Description
_id string The key's id. Pass it to the delete route.
name string The name you gave the key.
key string The x-key value. It isn't the id, so don't pass it to the delete route.
type string standard for keys you create.
team_id string Your account id.
scopes array The scopes the key holds.
mcp_client string The AI tool the key was created for in the dashboard's Connect AI flow, such as cursor. null otherwise.
created_at integer When the key was created, in epoch milliseconds.
expires_at integer When the key expires, in epoch milliseconds. null if it never does.
last_used_at integer The last authenticated request, in epoch milliseconds. null if never used.
request_count integer Authenticated requests made with the key.

Create a key

POST /register/public/apikeys creates a key and returns its secret. Your account must be verified first.

curl -X POST https://api-v2.dropcowboy.com/register/public/apikeys \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order notifications",
    "scopes": ["contacts:read", "sms:send"]
  }'
Field Type Required Description
name string No A name to recognise the key by. Defaults to "API Key - " plus today's date.
scopes array No The scopes the new key holds. Defaults to the scopes of the credential making the request.
expires_in_seconds integer No Delete the key automatically after this many seconds, from 300 to 31536000 (one year). Omit it for a key that never expires.

The response is the key's fields from the list, plus secret. Store the secret now. It isn't shown again.

{
  "data": {
    "_id": "8d2f6b9e-4a1c-4e7b-9f3d-5c8a2e6b1d74",
    "name": "Order notifications",
    "key": "2a6d9f3b-8e1c-4b5a-9d7f-3e6b1c8a4f92",
    "secret": "5e9b2d7f-3a8c-4f1e-b6d2-8c4a1f7e3b59",
    "type": "standard",
    "team_id": "4e9a2c7f-1b5d-4f3e-8c6a-9d2b7e4f1a53",
    "scopes": ["contacts:read", "sms:send"],
    "mcp_client": null,
    "created_at": 1774041600000,
    "expires_at": null
  },
  "meta": { "request_id": "c2a6e8f4-3b9d-4c1e-8a7f-5d3b1e9c6a24" }
}

A new key can't hold a scope the credential creating it lacks. Asking for one returns 403.

Short-lived keys for agents and scripts

Give an AI agent, a CI job or a one-off script its own key, with only the scopes it needs and an expiry:

curl -X POST https://api-v2.dropcowboy.com/register/public/apikeys \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly contact sync",
    "scopes": ["contacts:read", "lists:read"],
    "expires_in_seconds": 3600
  }'

When it expires, the key stops working and requests with it get 401. It's deleted permanently, usually within two minutes, and then drops out of the list.

Delete a key

DELETE /register/public/apikeys/{key_id} deletes a key. It stops working immediately. Pass the key's _id, not its key value.

curl -X DELETE https://api-v2.dropcowboy.com/register/public/apikeys/3b8e1f6a-9c4d-4a2e-b7f5-1d6c8a3e9b47 \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
{
  "data": { "deleted": true },
  "meta": { "request_id": "c2a6e8f4-3b9d-4c1e-8a7f-5d3b1e9c6a24" }
}

An unknown id returns 404.

Rotate a key

  1. Create a new key.
  2. Deploy it everywhere the old key is used.
  3. Watch the old key's last_used_at in the list until it stops changing.
  4. Delete the old key.

OAuth 2.0 user sign-in

AI tools connect through the Drop Cowboy® MCP server by having a user sign in, using the OAuth authorization code flow with PKCE. The tool then holds a bearer token that acts as that user on their account. Its scopes come from the user's AI access on their role: full access gets the standard scopes, view-only access gets the read scopes, and a user with no AI access can't connect. The MCP server handles the sign-in for you; the dashboard's Connect AI page shows how to add it to each tool.

For a tool that can't sign in, create a key on the Connect AI page instead. It's an API key whose scopes are capped by the same AI access.

OAuth 2.0 client credentials

Machine clients belong to one account and are set up by Drop Cowboy support; there's no self-serve way to create one yet. For your own servers, an API key does the same job. Request a token with the audience set to the API host:

curl https://login.dropcowboy.com/oauth/token \
  -d grant_type=client_credentials \
  -d client_id="$DC_CLIENT_ID" \
  -d client_secret="$DC_CLIENT_SECRET" \
  -d audience=https://api-v2.dropcowboy.com

Send the access_token from the response as a bearer credential:

curl "https://api-v2.dropcowboy.com/contact/public/contacts?limit=10" \
  -H "Authorization: Bearer $DC_TOKEN"

The token holds the scopes granted to your client. Each route needs one of the scopes listed for it. Reuse a token until it expires, then request a new one.

The send routes check credentials after the 202

POST /rvm, /sms, /voice-broadcast and /ai-broadcast answer 202 before they check your credential, so a wrong key arrives later as outcome 3007 on your callback_url. See What a 202 means.

Scopes

A scope grants access to a group of routes. API keys and OAuth tokens both carry scopes. Each route accepts the scopes listed in its page's routes table, and a credential needs any one of them.

Scope Grants
contacts:read Contacts, notes, follow-ups, custom fields, tags, timelines, call history, text history, email history, documents, Inbox Tasks, templates, users, pipelines
contacts:write Create and change contacts, tags, notes, follow-ups, documents, Inbox Tasks and pipelines
lists:read Contact lists and their members
lists:write Create and change lists, and add or remove members
campaigns:read Campaigns and their stats
campaigns:write Create, update and delete campaigns
campaigns:send Start and pause campaigns
rvm:send Send ringless voicemail
sms:send Send texts and reply to a text
voice:send Send voice broadcasts and AI calls, and synthesize speech
email:send Send email
email:read Sending mailboxes
media:read Media, voices and transcription
media:write Upload media, and clone, design and delete voices
webhooks:read Webhook subscriptions, signing secrets and event types
webhooks:write Subscribe and unsubscribe webhooks
balance:read Account details, balance, API keys, Detection keys and Building Blocks readiness
account:write Create and delete API keys and Detection keys, enable Building Blocks, connect a carrier
agents:read AI agents, agent templates and knowledge bases
agents:write Create, change, publish and delete AI agents and knowledge bases
consent:read Consent records
consent:write Record and revoke consent
dnc:read Check the do-not-contact list
dnc:write Add to and remove from the do-not-contact list
numbers:read Phone numbers, phone lines and IVRs
numbers:write Rent numbers, manage phone lines, mint site tokens
chat:read Web chat sites
chat:write Reply to a web chat

Give each key only the scopes it needs. The OpenAPI spec lists the exact scopes for every route.

Embed site tokens

Building Blocks widgets run in a browser, so they never see your API key. Your server mints a short-lived site token with POST /phone/public/embed/token (scope numbers:write) and hands it to the page.

curl -X POST https://api-v2.dropcowboy.com/phone/public/embed/token \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "9d3f7b1e-2c8a-4f5d-b6e9-4a1c7d3f8b25",
    "sub": "agent-42",
    "scope": "dialer:webrtc",
    "ttl_seconds": 900
  }'
Field Type Required Description
site_id string No The embed site from the dashboard.
sub string No Your own id for the visitor or agent using the widget.
scope string or array No What the widget may do: dialer:webrtc, phone:hub, contacts, campaigns, media, voice or detection. Separate several with spaces, or pass an array. Defaults to dialer:webrtc.
ttl_seconds integer No How long the token lasts, up to 3600. Defaults to 3600.
{
  "data": {
    "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20vIiwic3ViIjoiYWdlbnQtNDIifQ.c2lnbmF0dXJl",
    "expires_at": 1774042500000,
    "jti": "1d5b9f3e-7a2c-4e8d-b6f1-3c7a9e5d2b84",
    "pool_id": "6e1f3a5c-9b2d-4c8e-a7f1-3d5b9c2e4a61"
  },
  "meta": { "request_id": "c2a6e8f4-3b9d-4c1e-8a7f-5d3b1e9c6a24" }
}
Field Type Description
token string The site token to give the widget.
expires_at integer When it expires, in epoch milliseconds.
jti string The token's unique id.
pool_id string Your carrier connection, when you have one.

dialer:webrtc and phone:hub tokens place calls on your own carrier, so they need a connected carrier and funds. Account shows how to check both. A site token can't mint another site token, and the send routes don't accept one.

Errors

Status When What to do
401 No credential, a wrong key or secret, or a deleted, expired or invalid credential Check the headers, or create a new key or token
403 The credential lacks every scope the route accepts. detail names them. Use a credential with one of those scopes
403 Creating a key on an unverified account, or asking for scopes you lack Verify the account, or ask for fewer scopes
403 A dialer site token without a connected carrier Connect your carrier
402 A dialer site token without funds Add funds
400 A bad scopes list or expires_in_seconds when creating a key Fix the field named in detail
404 Deleting a key that doesn't exist Check the _id

Errors use the format in Responses, errors and limits.


API access is subject to rate limits and usage policies. API availability, endpoints, and features may change with notice. Breaking changes will be communicated via changelog with migration period when possible. API keys must be kept secure. Customers are responsible for all activity under their API credentials.

Code samples

The requests from this page, ready to copy. Set DC_KEY and DC_SECRET to your API key pair first.

API keys
export DC_KEY="7f3c9a1e-5b2d-4e8f-a6c4-9d1b3e7f5a20"
export DC_SECRET="c8e2a4f6-1d3b-4a9c-8e7f-2b5d9a1c6e34"

curl https://api-v2.dropcowboy.com/register/public/account \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
List keys
curl https://api-v2.dropcowboy.com/register/public/apikeys \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
Create a key
curl -X POST https://api-v2.dropcowboy.com/register/public/apikeys \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order notifications",
    "scopes": ["contacts:read", "sms:send"]
  }'
Short-lived keys for agents and scripts
curl -X POST https://api-v2.dropcowboy.com/register/public/apikeys \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly contact sync",
    "scopes": ["contacts:read", "lists:read"],
    "expires_in_seconds": 3600
  }'
Delete a key
curl -X DELETE https://api-v2.dropcowboy.com/register/public/apikeys/3b8e1f6a-9c4d-4a2e-b7f5-1d6c8a3e9b47 \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
OAuth 2.0 client credentials
curl https://login.dropcowboy.com/oauth/token \
  -d grant_type=client_credentials \
  -d client_id="$DC_CLIENT_ID" \
  -d client_secret="$DC_CLIENT_SECRET" \
  -d audience=https://api-v2.dropcowboy.com
OAuth 2.0 client credentials (2)
curl "https://api-v2.dropcowboy.com/contact/public/contacts?limit=10" \
  -H "Authorization: Bearer $DC_TOKEN"
Embed site tokens
curl -X POST https://api-v2.dropcowboy.com/phone/public/embed/token \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "9d3f7b1e-2c8a-4f5d-b6e9-4a1c7d3f8b25",
    "sub": "agent-42",
    "scope": "dialer:webrtc",
    "ttl_seconds": 900
  }'