SuperCoolDocs Get an API key

Reference

API reference

Every endpoint of the SuperCool API: parameters, request and response schemas, errors, and curl, Python and JavaScript samples.

View as Markdown

Base URL: https://api.supercool.com/v1. Authenticate with Authorization: Bearer sc_key_… (Authentication). Requests and responses are JSON; errors share one shape. Download the spec as OpenAPI JSON or YAML.

#Messages

Talk to your agent and follow the work it starts.

#Send a message

POST /v1/messages

Sends your agent a message, optionally with files. The call returns within about 30 seconds: either the finished answer, or status: "processing" when the agent started longer work. Follow a processing message with GET /messages/{message_id} (use ?wait= to long-poll), its events, or a webhook.

Returns 201 for a new message and 200 when an Idempotency-Key replays an existing one. Running out of credits is not an error: the message comes back with status: "blocked" and reason: "out_of_credits". Neither is the running-jobs cap: work the agent can't start because 5 jobs are already running comes back as a blocked job with reason: "too_many_running", and the message lists the running work.

Parameters

NameTypeDescription
Idempotency-Keystring headerAny string up to 128 characters, unique per logical request. Scoped to the API key and kept for 7 days: sending it again with the same body returns the same message (200) and never starts work twice; with a different body it's 409 idempotency_conflict.

Request body MessageCreate

FieldTypeDescription
messagestringWhat you want, in plain language. Up to 20,000 characters; send longer text as a file.
filesarray of InlineFileSmall files inline (up to 5, 10 MB each, 25 MB in total). Each has a url or base64. A file that can't be fetched doesn't fail the message: it's listed in the message's notes.
upload_idsarray of stringFinished uploads to attach (up to 10, 1 GB in total). See POST /uploads.
webhook_endpoint_idstring | nullSend this message's webhook events to this registered endpoint (whe_…) instead of the account's endpoints.

Responses

  • 200 An Idempotency-Key you already used with the same body. The same message, as it is now. Returns Message.
  • 201 The message was accepted. It is either final already or processing. Returns Message.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 409 The request conflicts with the current state.
  • 413 A file or upload is over the size limit.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 400 empty_message 400 too_long 400 too_many 401 unauthorized 404 not_found 409 idempotency_conflict 409 upload_not_ready 413 too_large 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/messages" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8812-ad" \
  -d '{
    "message": "A 15 second vertical ad for my candle shop, warm and cozy"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/messages",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}", "Idempotency-Key": "order-8812-ad"},
    json={
        "message": "A 15 second vertical ad for my candle shop, warm and cozy",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "order-8812-ad",
  },
  body: JSON.stringify({
    "message": "A 15 second vertical ad for my candle shop, warm and cozy"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

#Get a message

GET /v1/messages/{message_id}

One message: its status, the agent's reply, the jobs it started and the files they made. With wait, the call holds for up to that many seconds and returns as soon as something changes (the status, the reply arriving, or a job's status) or the message is final.

Messages are kept for at least 14 days after their last job's deadline. After that this returns 404 not_found; read the work instead (GET /work/{work_id}).

Parameters

NameTypeDescription
message_id requiredstring pathThe message's id (msg_…).
waitinteger queryLong-poll for up to this many seconds (0 to 45). 0 returns immediately. Default 0.

Responses

  • 200 The message. Returns Message.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d?wait=30" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    params={"wait": 30},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d?wait=30", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d",
  "object": "message",
  "status": "completed",
  "reason": null,
  "question": null,
  "final": true,
  "reply": "On it. I'm making a 15 second vertical ad with warm candlelight shots.",
  "jobs": [
    {
      "job_id": "exe_9b2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e",
      "kind": "execution",
      "role": "execution",
      "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "title": "Candle shop vertical ad",
      "status": "completed",
      "reason": null,
      "final": true,
      "queued": null,
      "resume_by": null,
      "link": "https://supercool.com/chat/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "started_at": "2026-09-30T14:02:11.402000Z",
      "settled_at": "2026-09-30T14:06:40.221000Z",
      "files": [
        {
          "file_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4",
          "name": "candle_ad.mp4",
          "content_type": "video/mp4",
          "kind": "video",
          "size": 4812345,
          "url": "https://api.supercool.sh/api/v1/files/s/eyJmIjoiNWQwYzNl...",
          "revision": "\"5b1c0e6f9d2a4b7c8e1f3a5d7c9b0e2f\"",
          "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
          "label": null
        }
      ]
    }
  ],
  "files": [
    {
      "file_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4",
      "name": "candle_ad.mp4",
      "content_type": "video/mp4",
      "kind": "video",
      "size": 4812345,
      "url": "https://api.supercool.sh/api/v1/files/s/eyJmIjoiNWQwYzNl...",
      "revision": "\"5b1c0e6f9d2a4b7c8e1f3a5d7c9b0e2f\"",
      "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "label": null
    }
  ],
  "notes": [],
  "credits_used": 42.5,
  "created_at": "2026-09-30T14:02:03.118000Z",
  "updated_at": "2026-09-30T14:02:12.950000Z"
}

#List a message's events

GET /v1/messages/{message_id}/events

The message's update log, oldest first: progress steps, late replies, each job's result with its files, and state changes. Only this message's entries: another message's progress in the same chat isn't included. Pass back the cursor from the previous page to get only what's new. With no cursor, the log starts where the message started. done is true once the message has nothing more to report.

The call waits at least one second, and up to wait seconds, for new entries. Only one wait per message runs at a time (409 poll_in_progress otherwise). Entries are kept for 7 days; a cursor older than that returns cursor_expired: true with a fresh cursor.

Parameters

NameTypeDescription
message_id requiredstring pathThe message's id (msg_…).
cursorstring queryThe cursor from the previous page. Opaque.
waitinteger queryWait up to this many seconds for new entries (0 to 45; at least 1 second is always waited). Default 0.

Responses

  • 200 A page of events. Returns EventList.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 409 The request conflicts with the current state.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 400 bad_cursor 401 unauthorized 404 not_found 409 poll_in_progress 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/events?wait=30" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/events",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    params={"wait": 30},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/events?wait=30", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "list",
  "data": [
    {
      "seq": 118,
      "type": "progress",
      "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "message_id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d",
      "at": "2026-09-30T14:03:02.000000Z",
      "step": "Rendering the video",
      "status": "started",
      "file_names": []
    },
    {
      "seq": 121,
      "type": "result",
      "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "message_id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d",
      "at": "2026-09-30T14:06:40.000000Z",
      "title": "Candle shop vertical ad",
      "text": "Your 15 second ad is ready.",
      "outcome": "completed",
      "files": []
    }
  ],
  "has_more": false,
  "done": true,
  "cursor": "eyJjIjoiYXBpOnU..."
}

#Recover a message's results

POST /v1/messages/{message_id}/recover

For work that outlived its watch (a job or message that expired): re-arms the watch so the result is looked up again and lands in the message, its events and your webhooks. It never resends the message or starts new work. One entry per piece of work: settled (already has its outcome), watching (still being watched), recovering (re-armed now) or exhausted (recovered too many times; read the work).

Parameters

NameTypeDescription
message_id requiredstring pathThe message's id (msg_…).

Responses

  • 200 What happened to each piece of work. Returns Recovery.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/recover" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/recover",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/recover", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d",
  "object": "recovery",
  "work": [
    {
      "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "state": "recovering"
    }
  ]
}

#Cancel a message's work

POST /v1/messages/{message_id}/cancel

Stops the work a message started. Work still queued (for credits, or for its chat) is removed before it runs; work running now is stopped when it is this message's own run (other work in the same chat is never stopped) and settles as cancelled a moment later with the credits it used; work the agent starts for the message after this is cancelled as it arrives. Credits already used stay on credits_used. Returns the message, plus cancelled (job ids stopped) and not_cancellable (job ids that couldn't be, such as a follow-up that joined another message's running work). Cancelling finished work changes nothing.

Parameters

NameTypeDescription
message_id requiredstring pathThe message's id (msg_…).

Responses

  • 200 The message after cancelling. Returns object.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/cancel" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/cancel",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/cancel", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

#Uploads

Attach large files (up to 500 MB each) to a message.

#Start an upload

POST /v1/uploads

Starts a large upload (up to 500 MB). Returns a presigned POST: send a multipart/form-data POST to url with every entry of fields as form fields and the file last, as a field named file. The link is valid for 15 minutes. Then call POST /uploads/{upload_id}/complete and pass the upload_id in a message's upload_ids. Uploads never attached to a message are deleted after 24 hours.

Request body UploadCreate

FieldTypeDescription
filename requiredstring
size requiredintegerThe exact size in bytes (up to 500 MB). The upload is capped at this size.
content_typestring | nullThe MIME type. Guessed from the file name when omitted. The POST must send the same Content-Type field (it is in fields).

Responses

  • 201 The upload was created. POST the file to url next. Returns UploadCreated.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 413 A file or upload is over the size limit.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 400 bad_size 401 unauthorized 413 too_large 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/uploads" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "product-shoot.mov",
    "size": 157286400,
    "content_type": "video/quicktime"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/uploads",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "filename": "product-shoot.mov",
        "size": 157286400,
        "content_type": "video/quicktime",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/uploads", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "filename": "product-shoot.mov",
    "size": 157286400,
    "content_type": "video/quicktime"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 201

json
{
  "object": "upload",
  "upload_id": "up_0a1b2c3d4e5f60718293a4b5c6d7e8f9",
  "url": "https://supercool-uploads.s3.amazonaws.com/",
  "fields": {
    "key": "agent-uploads/incoming/.../product-shoot.mov",
    "Content-Type": "video/quicktime",
    "policy": "eyJleHBpcmF0aW9uIjoi...",
    "x-amz-signature": "3f1c..."
  },
  "expires_at": "2026-09-30T14:17:03.118000Z",
  "max_bytes": 157286400
}

#Complete an upload

POST /v1/uploads/{upload_id}/complete

Finalizes an upload after the file was POSTed to the presigned url. The file is checked (its real size, and executables are refused) and becomes ready to attach. Calling it again on a ready upload returns the same result.

Parameters

NameTypeDescription
upload_id requiredstring pathThe upload_id from POST /uploads (up_…).

Responses

  • 200 The upload is ready to attach. Returns Upload.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 409 The request conflicts with the current state.
  • 413 A file or upload is over the size limit.
  • 422 The uploaded file was refused (an executable).
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 409 not_pending 409 not_uploaded 409 changed 413 too_large 422 executable_refused 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/uploads/up_0a1b2c3d4e5f60718293a4b5c6d7e8f9/complete" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/uploads/up_0a1b2c3d4e5f60718293a4b5c6d7e8f9/complete",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/uploads/up_0a1b2c3d4e5f60718293a4b5c6d7e8f9/complete", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "upload",
  "upload_id": "up_0a1b2c3d4e5f60718293a4b5c6d7e8f9",
  "status": "ready",
  "filename": "product-shoot.mov",
  "content_type": "video/quicktime",
  "size": 157286400
}

#Work

The chats your agent works in, the same ones you see in the app.

#List recent work

GET /v1/work

Your agent's recent work (chats), newest first: the same list you see in the app, whether the work was started over the API, in the app, by phone or anywhere else.

Parameters

NameTypeDescription
limitinteger queryHow many to return (1 to 50). Default 20.

Responses

  • 200 Recent work. Returns WorkList.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 401 unauthorized 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/work?limit=10" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/work",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    params={"limit": 10},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/work?limit=10", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "list",
  "data": [
    {
      "object": "work",
      "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
      "title": "Candle shop vertical ad",
      "preview": "Your 15 second ad is ready.",
      "link": "https://supercool.com/chat/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f"
    }
  ]
}

#Get a piece of work

GET /v1/work/{work_id}

One piece of work: whether it's running, the latest result text (paged with from_char / next_from_char) and its most recent files (up to 8), each with a fresh download link.

Parameters

NameTypeDescription
work_id requiredstring pathThe work_id from a job, an event or GET /work.
from_charinteger queryRead the text from this character on (use the previous next_from_char). Default 0.

Responses

  • 200 The work. Returns Work.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/work/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/work/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/work/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "work",
  "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
  "title": "Candle shop vertical ad",
  "status": "idle",
  "text": "Your 15 second ad is ready. I used warm, slow push-ins on the candles...",
  "next_from_char": null,
  "link": "https://supercool.com/chat/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
  "files": []
}

#Files

Download what the agent made.

#Get a file

GET /v1/files/{file_id}

A file's record with a fresh signed download link. Use it when a url has expired, or when a download answered 412 because the file changed since the link was made. File ids contain slashes (<work_id>/<path>); put them in the path as they are.

Parameters

NameTypeDescription
file_id requiredstring pathThe file_id from a message, job, event or work (<work_id>/<path>, slashes included).

Responses

  • 200 The file. Returns FileObject.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/files/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/files/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/files/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "file",
  "file_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4",
  "name": "candle_ad.mp4",
  "content_type": "video/mp4",
  "kind": "video",
  "size": 4812345,
  "url": "https://api.supercool.sh/api/v1/files/s/eyJmIjoiNWQwYzNl...",
  "revision": "\"5b1c0e6f9d2a4b7c8e1f3a5d7c9b0e2f\"",
  "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
  "label": null
}

#Account

Who the key belongs to, the plan, credits and limits.

#Get the account

GET /v1/me

The account the API key belongs to, its plan, available credits, the agent's name, the key itself and your limits.

Responses

  • 200 The account. Returns Account.
  • 401 The API key is missing, unknown or revoked.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/me" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/me",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/me", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "account",
  "user_id": "64f1c2a9e4b0a1b2c3d4e5f6",
  "email": "[email protected]",
  "name": "Sam Rivera",
  "plan": "pro",
  "credits": 1840,
  "agent": {
    "name": "Nova"
  },
  "key": {
    "id": "pat:6a1b2c3d4e5f",
    "name": "prod server"
  },
  "limits": {
    "running_jobs": 5,
    "requests_per_minute": 600
  }
}

#Webhooks

Get pushed events instead of polling.

#List webhook endpoints

GET /v1/webhooks

The account's webhook endpoints (up to 10).

Responses

  • 200 The endpoints. Returns WebhookEndpointList.
  • 401 The API key is missing, unknown or revoked.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/webhooks" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/webhooks", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "list",
  "data": [
    {
      "object": "webhook_endpoint",
      "id": "whe_4c1d2e3f4a5b6c7d8e9f",
      "url": "https://example.com/hooks/supercool",
      "events": [
        "*"
      ],
      "description": "prod",
      "created_at": "2026-09-30T13:40:00.000000Z",
      "disabled_at": null,
      "disabled_reason": null,
      "failing_since": null
    }
  ]
}

#Create a webhook endpoint

POST /v1/webhooks

Registers a public https URL (port 443 or 8443) to receive events. The response includes the endpoint's signing secret (whsec_…). It is shown only this once: store it now. Omit events (or pass ["*"]) to receive every event.

Request body WebhookCreate

FieldTypeDescription
url requiredstringA public https URL (port 443 or 8443, up to 2,000 characters).
eventsarray of string | nullEvents to receive, or ["*"] for all (the default).
descriptionstring | nullUp to 120 characters.

Responses

  • 201 The endpoint, with its secret (shown once). Returns WebhookEndpointWithSecret.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 409 The request conflicts with the current state.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 400 bad_url 400 bad_events 400 bad_request 401 unauthorized 409 too_many_endpoints 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/webhooks" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/supercool",
    "events": [
      "message.completed",
      "message.failed",
      "message.blocked"
    ],
    "description": "prod"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "url": "https://example.com/hooks/supercool",
        "events": [
            "message.completed",
            "message.failed",
            "message.blocked",
        ],
        "description": "prod",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "url": "https://example.com/hooks/supercool",
    "events": [
      "message.completed",
      "message.failed",
      "message.blocked"
    ],
    "description": "prod"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 201

json
{
  "object": "webhook_endpoint",
  "id": "whe_4c1d2e3f4a5b6c7d8e9f",
  "url": "https://example.com/hooks/supercool",
  "events": [
    "message.completed",
    "message.failed",
    "message.blocked"
  ],
  "description": "prod",
  "created_at": "2026-09-30T13:40:00.000000Z",
  "disabled_at": null,
  "disabled_reason": null,
  "failing_since": null,
  "secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"
}

#Get a webhook endpoint

GET /v1/webhooks/{endpoint_id}

One endpoint and its 20 most recent deliveries.

Parameters

NameTypeDescription
endpoint_id requiredstring pathThe webhook endpoint's id (whe_…).

Responses

  • 200 The endpoint. Returns WebhookEndpointDetail.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "webhook_endpoint",
  "id": "whe_4c1d2e3f4a5b6c7d8e9f",
  "url": "https://example.com/hooks/supercool",
  "events": [
    "*"
  ],
  "description": "prod",
  "created_at": "2026-09-30T13:40:00.000000Z",
  "disabled_at": null,
  "disabled_reason": null,
  "failing_since": null,
  "recent_deliveries": [
    {
      "object": "webhook_delivery",
      "id": "evt_7d8e9f0a1b2c3d4e5f6a7b8c",
      "event": "message.completed",
      "status": "delivered",
      "attempts": 1,
      "last_status_code": 200,
      "last_error": null,
      "last_response": "ok",
      "last_ms": 184,
      "created_at": "2026-09-30T14:06:41.000000Z",
      "delivered_at": "2026-09-30T14:06:41.000000Z",
      "next_attempt_at": "2026-09-30T14:06:41.000000Z",
      "message_id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d"
    }
  ]
}

#Update a webhook endpoint

PATCH /v1/webhooks/{endpoint_id}

Changes an endpoint's URL, events or description, or turns it off and on. Send only the fields to change. "enabled": true turns a disabled endpoint back on (after you fixed it) and clears its failure state.

Parameters

NameTypeDescription
endpoint_id requiredstring pathThe webhook endpoint's id (whe_…).

Request body WebhookUpdate

FieldTypeDescription
urlstring | null
eventsarray of string | null
descriptionstring | null
enabledboolean | nulltrue turns the endpoint back on and clears its failure state; false turns it off.

Responses

  • 200 The updated endpoint. Returns WebhookEndpoint.
  • 400 The request can't be processed as sent. invalid_request (with problems) when it doesn't match the schema.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

400 invalid_request 400 bad_url 400 bad_events 400 bad_request 401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl -X PATCH "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true
  }'
Python
import os
import requests

resp = requests.patch(
    "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "enabled": True,
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "enabled": true
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

#Delete a webhook endpoint

DELETE /v1/webhooks/{endpoint_id}

Deletes the endpoint. Deliveries still waiting to be sent to it are cancelled.

Parameters

NameTypeDescription
endpoint_id requiredstring pathThe webhook endpoint's id (whe_…).

Responses

  • 200 Deleted. Returns Deleted.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl -X DELETE "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.delete(
    "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "webhook_endpoint",
  "id": "whe_4c1d2e3f4a5b6c7d8e9f",
  "deleted": true
}

#Rotate a signing secret

POST /v1/webhooks/{endpoint_id}/rotate-secret

Replaces the endpoint's signing secret and returns the new one (shown once). Every delivery from now on is signed with the new secret, including retries of earlier events.

Parameters

NameTypeDescription
endpoint_id requiredstring pathThe webhook endpoint's id (whe_…).

Responses

  • 200 The new secret. Returns RotatedSecret.
  • 401 The API key is missing, unknown or revoked.
  • 404 Nothing with that id for this API key or account.
  • 429 A rate limit was hit. Wait Retry-After seconds.

Errors

401 unauthorized 404 not_found 429 rate_limited

Example request

curl
curl -X POST "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f/rotate-secret" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f/rotate-secret",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f/rotate-secret", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message} (${data.request_id})`);
console.log(data);

Example response 200

json
{
  "object": "webhook_endpoint",
  "id": "whe_4c1d2e3f4a5b6c7d8e9f",
  "secret": "whsec_Q2hhbmdlZC1zZWNyZXQtZXhhbXBsZTEy"
}

#CRM

The CRM API behind the SuperCool Zapier app: contacts, tags, opportunities, tasks, workflows, texts and emails, REST hook triggers and the Lead API. Base URL https://api.supercool.com/api/v1/crm-app (not /v1), the same API keys, X-Workspace-Id for a team's CRM, and errors shaped {"detail": …}. Guide: CRM.

#Test a key

GET /api/v1/crm-app/zapier/me

Checks the API key and returns who it belongs to and which CRM it reaches: the key owner's personal CRM, or the team named by X-Workspace-Id. Zapier calls it when an account is connected and shows label as the connection's name.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The key, its owner and the CRM. Returns CrmAccount.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/me" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/me",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/me", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "ok": true,
  "user_id": "64f1c2a9e4b0a1b2c3d4e5f6",
  "email": "[email protected]",
  "workspace_id": "6650a1b2c3d4e5f6a7b8c9d0",
  "team_name": "Rivera Studio",
  "role": "admin",
  "key": {
    "id": "pat:6a1b2c3d4e5f",
    "name": "Zapier",
    "hint": "sc_key_9fK2…x7Qa"
  },
  "label": "[email protected] · Rivera Studio"
}

#List triggers

GET /api/v1/crm-app/zapier/triggers

Every trigger key a REST hook can subscribe to, with the CRM event types it fires on. See the trigger catalogue.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The triggers. Returns CrmTriggerList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/triggers" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/triggers",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/triggers", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "key": "new_contact",
      "label": "New Contact",
      "noun": "Contact",
      "types": [
        "contact.created"
      ]
    },
    {
      "key": "lead_received",
      "label": "Lead Received",
      "noun": "Lead",
      "types": [
        "facebook.lead_received",
        "form.submitted"
      ]
    }
  ]
}

#List REST hook subscriptions

GET /api/v1/crm-app/zapier/hooks

The CRM's REST hook subscriptions, newest first, whichever key created them.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The subscriptions. Returns CrmHookList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/hooks" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/hooks",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/hooks", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "zh_4c1d2e3f4a5b6c7d8e9f",
      "key_id": "pat:6a1b2c3d4e5f",
      "event": "new_contact",
      "label": "New Contact",
      "target_url": "https://hooks.example.com/supercool/new-contact",
      "target_host": "hooks.example.com",
      "key_name": "Zapier",
      "key_hint": "sc_key_9fK2…x7Qa",
      "deliveries": 12,
      "failures": 0,
      "last_delivery_at": "2026-10-23T14:02:12.010000Z",
      "last_status": 200,
      "last_error": null,
      "created_at": "2026-10-23T13:40:00Z",
      "updated_at": "2026-10-23T14:02:12.010000Z"
    }
  ]
}

#Subscribe a REST hook

POST /api/v1/crm-app/zapier/hooks

Subscribes target_url to one trigger. Each matching CRM event is then POSTed to it (see Delivery). Subscribing the same URL to the same trigger again returns the existing subscription. Up to 100 subscriptions per CRM.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmHookCreate

FieldTypeDescription
event requiredstringA trigger key (GET /zapier/triggers), like new_contact.
target_url requiredstringA public http or https URL. Private and local addresses are refused. Up to 2048 characters.

Responses

  • 201 The subscription. Returns CrmHook.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 422 The body doesn't match the schema (a missing required field, a wrong type). detail lists the problems.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/hooks" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "new_contact",
    "target_url": "https://hooks.example.com/supercool/new-contact"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/hooks",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "event": "new_contact",
        "target_url": "https://hooks.example.com/supercool/new-contact",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/hooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "event": "new_contact",
    "target_url": "https://hooks.example.com/supercool/new-contact"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 201

json
{
  "id": "zh_4c1d2e3f4a5b6c7d8e9f",
  "key_id": "pat:6a1b2c3d4e5f",
  "event": "new_contact",
  "label": "New Contact",
  "target_url": "https://hooks.example.com/supercool/new-contact",
  "target_host": "hooks.example.com",
  "key_name": "Zapier",
  "key_hint": "sc_key_9fK2…x7Qa",
  "deliveries": 0,
  "failures": 0,
  "last_delivery_at": null,
  "last_status": null,
  "last_error": null,
  "created_at": "2026-10-23T13:40:00Z",
  "updated_at": "2026-10-23T13:40:00Z"
}

#Unsubscribe by id or URL

DELETE /api/v1/crm-app/zapier/hooks

Removes a subscription by id, or every subscription to target_url (only the one for event, if you send it). Events still waiting to be delivered to them are dropped. 400 without id or target_url; 404 when nothing matches.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmHookDelete

FieldTypeDescription
idstringThe subscription's id (zh_…).
target_urlstringRemove every subscription to this URL.
eventstringWith target_url, only the subscription for this trigger.

Responses

  • 200 Removed. Returns CrmOk.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X DELETE "https://api.supercool.com/api/v1/crm-app/zapier/hooks" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target_url": "https://hooks.example.com/supercool/new-contact",
    "event": "new_contact"
  }'
Python
import os
import requests

resp = requests.delete(
    "https://api.supercool.com/api/v1/crm-app/zapier/hooks",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "target_url": "https://hooks.example.com/supercool/new-contact",
        "event": "new_contact",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/hooks", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "target_url": "https://hooks.example.com/supercool/new-contact",
    "event": "new_contact"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "ok": true
}

#Unsubscribe a REST hook

DELETE /api/v1/crm-app/zapier/hooks/{hook_id}

Removes one subscription. Events still waiting to be delivered to it are dropped.

Parameters

NameTypeDescription
hook_id requiredstring pathThe subscription's id (zh_…).
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 Removed. Returns CrmOk.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X DELETE "https://api.supercool.com/api/v1/crm-app/zapier/hooks/zh_4c1d2e3f4a5b6c7d8e9f" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.delete(
    "https://api.supercool.com/api/v1/crm-app/zapier/hooks/zh_4c1d2e3f4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/hooks/zh_4c1d2e3f4a5b6c7d8e9f", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "ok": true
}

#Get sample events

GET /api/v1/crm-app/zapier/perform-list/{event}

Up to 3 of the CRM's most recent events for a trigger, newest first, in the payload a subscription receives. With none yet, one made-up sample ("id": "ev_sample").

Parameters

NameTypeDescription
event requiredstring pathA trigger key, such as new_contact. See GET /zapier/triggers.
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The events. Returns CrmEventList.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/perform-list/new_contact" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/perform-list/new_contact",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/perform-list/new_contact", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "ev_3f9a1c2e7b8d4e5f6a7b",
      "trigger": "tag_added",
      "type": "contact.tag_added",
      "label": "Contact tag added",
      "occurred_at": "2026-10-23T14:02:11.402000Z",
      "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
      "contact": {
        "id": "ct_8f2a6c1e9b3d5f7a0c4e",
        "email": "[email protected]",
        "phone": "+15555550123",
        "first_name": "Jane",
        "last_name": "Doe",
        "full_name": "Jane Doe",
        "company": "Example Co",
        "address": null,
        "city": "Austin",
        "state": "TX",
        "postal_code": "78701",
        "country": "US",
        "website": null,
        "source": "zapier",
        "owner_user_id": null,
        "fields": {},
        "tag_ids": [
          "tag_1a2b3c4d5e6f7a8b9c0d"
        ],
        "tags": [
          "lead"
        ],
        "dnd": {
          "email": false,
          "sms": false,
          "calls": false,
          "all": false,
          "reason": null,
          "at": null
        },
        "consent": {
          "email": "explicit",
          "sms": null
        },
        "created_at": "2026-10-23T14:02:11.402000Z",
        "updated_at": "2026-10-23T14:02:11.402000Z"
      },
      "data": {
        "tag_id": "tag_1a2b3c4d5e6f7a8b9c0d",
        "tag_name": "lead"
      },
      "ref": {},
      "actor": {
        "kind": "api_key",
        "id": "pat:6a1b2c3d4e5f"
      }
    }
  ]
}

#Create or update a contact

POST /api/v1/crm-app/zapier/contacts

Finds the contact by email, then phone, and updates it, or creates it (source zapier). Only non-empty fields are written. consent: true records explicit email consent and sms_consent: true explicit SMS consent; explicit consent is never downgraded. Returns the contact at the top level, with ok and created.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmContactWrite

FieldTypeDescription
contact_idstringUpdate only. The contact's id.
contactstringUpdate only. An id, email address or phone number.
emailstring
phonestringStored in E.164. US numbers without a country code get +1.
first_namestring
last_namestring
companystring
addressstring
citystring
statestring
postal_codestring
countrystring
websitestring
owner_user_idstringA team member's user id (GET /zapier/team).
tagsarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).
fieldsobjectCustom field values by key (GET /zapier/fields). Create or update ignores unknown keys; update refuses them.
consentbooleanCreate or update only. true records explicit email consent. Otherwise email consent is imported_unknown, unless already explicit.
sms_consentbooleanCreate or update only. true records explicit SMS consent, if the contact has a phone number.

Responses

  • 200 The contact, created or updated. Returns CrmContactResult.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 422 The body doesn't match the schema (a missing required field, a wrong type). detail lists the problems.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/contacts" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "first_name": "Jane",
    "last_name": "Doe",
    "phone": "(555) 555-0123",
    "tags": "lead, webinar",
    "consent": true
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/contacts",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "email": "[email protected]",
        "first_name": "Jane",
        "last_name": "Doe",
        "phone": "(555) 555-0123",
        "tags": "lead, webinar",
        "consent": True,
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/contacts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "[email protected]",
    "first_name": "Jane",
    "last_name": "Doe",
    "phone": "(555) 555-0123",
    "tags": "lead, webinar",
    "consent": true
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "ok": true,
  "created": true,
  "id": "ct_8f2a6c1e9b3d5f7a0c4e",
  "email": "[email protected]",
  "phone": "+15555550123",
  "first_name": "Jane",
  "last_name": "Doe",
  "company": null,
  "display_name": "Jane Doe",
  "tags": [
    "tag_1a2b3c4d5e6f7a8b9c0d",
    "tag_0d9c8b7a6f5e4d3c2b1a"
  ],
  "fields": {},
  "source": "zapier",
  "owner_user_id": null,
  "status": "active",
  "dnd": {
    "email": false,
    "sms": false,
    "calls": false,
    "all": false,
    "reason": null,
    "at": null
  },
  "consent": {
    "email": {
      "status": "explicit",
      "source": "zapier",
      "at": "2026-10-23T14:02:11.402000Z"
    },
    "call": {
      "status": "none",
      "source": null,
      "at": null
    }
  },
  "last_activity_at": "2026-10-23T14:02:11.402000Z",
  "created_at": "2026-10-23T14:02:11.402000Z",
  "updated_at": "2026-10-23T14:02:11.402000Z"
}

#Update a contact

POST /api/v1/crm-app/zapier/contacts/update

Changes an existing contact, found by contact_id, contact, email or phone. Only the fields you send change; tags are added. Custom field keys must exist. consent and sms_consent are ignored. 400 Nothing to update without changes.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmContactWrite

FieldTypeDescription
contact_idstringUpdate only. The contact's id.
contactstringUpdate only. An id, email address or phone number.
emailstring
phonestringStored in E.164. US numbers without a country code get +1.
first_namestring
last_namestring
companystring
addressstring
citystring
statestring
postal_codestring
countrystring
websitestring
owner_user_idstringA team member's user id (GET /zapier/team).
tagsarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).
fieldsobjectCustom field values by key (GET /zapier/fields). Create or update ignores unknown keys; update refuses them.
consentbooleanCreate or update only. true records explicit email consent. Otherwise email consent is imported_unknown, unless already explicit.
sms_consentbooleanCreate or update only. true records explicit SMS consent, if the contact has a phone number.

Responses

  • 200 The updated contact, at the top level, with ok. Returns CrmContactResult.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 422 The body doesn't match the schema (a missing required field, a wrong type). detail lists the problems.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/contacts/update" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": "[email protected]",
    "company": "Example Co",
    "tags": [
      "customer"
    ]
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/contacts/update",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "contact": "[email protected]",
        "company": "Example Co",
        "tags": [
            "customer",
        ],
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/contacts/update", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "contact": "[email protected]",
    "company": "Example Co",
    "tags": [
      "customer"
    ]
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

#Find a contact

GET /api/v1/crm-app/zapier/contacts/find

Looks a contact up by id, then email, then phone. No match is an empty list, never a 404.

Parameters

NameTypeDescription
idstring queryThe contact's id (ct_…).
contactstring queryAn id, email address or phone number.
emailstring queryThe contact's email address.
phonestring queryThe contact's phone number.
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The contact, or an empty list. Returns CrmContactList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/contacts/find?email=jane%40example.com" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/contacts/find",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    params={"email": "[email protected]"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/contacts/find?email=jane%40example.com", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "ct_8f2a6c1e9b3d5f7a0c4e",
      "email": "[email protected]",
      "phone": "+15555550123",
      "first_name": "Jane",
      "last_name": "Doe",
      "display_name": "Jane Doe",
      "tags": [],
      "fields": {},
      "source": "zapier",
      "status": "active"
    }
  ]
}

#Add tags to a contact

POST /api/v1/crm-app/zapier/contacts/tags

Adds tags by name; missing tags are created. Tags the contact already has change nothing.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmTagsChange

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
tags requiredarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).

Responses

  • 200 The contact, at the top level, with ok. Returns CrmContactResult.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/contacts/tags" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
    "tags": [
      "vip",
      "webinar"
    ]
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/contacts/tags",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
        "tags": [
            "vip",
            "webinar",
        ],
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/contacts/tags", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
    "tags": [
      "vip",
      "webinar"
    ]
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

#Remove tags from a contact

POST /api/v1/crm-app/zapier/contacts/tags/remove

Removes tags given by name (ignoring case and extra spaces) or id (tag_…). Tags the contact doesn't have are skipped.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmTagsChange

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
tags requiredarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).

Responses

  • 200 The contact, at the top level, with ok. Returns CrmContactResult.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/contacts/tags/remove" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "tags": "webinar"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/contacts/tags/remove",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "email": "[email protected]",
        "tags": "webinar",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/contacts/tags/remove", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "[email protected]",
    "tags": "webinar"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

#Create an opportunity

POST /api/v1/crm-app/zapier/opportunities

Opens an opportunity for a contact, in a pipeline and stage given by id or name (the default pipeline and its first stage when omitted). One open opportunity per contact per pipeline unless allow_duplicate.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmOpportunityCreate

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
pipeline_idstringThe pipeline's id (pl_…). With neither this nor pipeline, the default pipeline.
pipelinestringThe pipeline's id or name.
stage_idstringThe stage's id (st_…) or name. With neither this nor stage, the first stage.
stagestringThe stage's id or name (any case).
namestringDefaults to the contact's name.
valuenumber or stringA number is in minor units (150000 is 1,500.00); a string is in major units ("1,500.00").
currencystringA 3-letter code. Defaults to the CRM's currency.
owner_user_idstringA team member's user id. Ignored if they're not on the team.
tagsarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).
fieldsobjectOpportunity custom field values by key.
expected_close_atstringYYYY-MM-DD.
allow_duplicatebooleanAllow a second open opportunity for the contact in this pipeline.

Responses

  • 201 The opportunity. Returns CrmOpportunity.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 422 The body doesn't match the schema (a missing required field, a wrong type). detail lists the problems.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/opportunities" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "pipeline": "Sales",
    "stage": "Qualified",
    "value": "1500"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/opportunities",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "email": "[email protected]",
        "pipeline": "Sales",
        "stage": "Qualified",
        "value": "1500",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/opportunities", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "[email protected]",
    "pipeline": "Sales",
    "stage": "Qualified",
    "value": "1500"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 201

json
{
  "id": "opp_7d8e9f0a1b2c3d4e5f6a",
  "pipeline_id": "pl_2b3c4d5e6f7a8b9c0d1e",
  "stage_id": "st_5e6f7a8b9c0d1e2f3a4b",
  "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
  "name": "Jane Doe",
  "status": "open",
  "value": 150000,
  "currency": "usd",
  "source": "zapier",
  "owner_user_id": null,
  "followers": [],
  "tags": [],
  "fields": {},
  "lost_reason": null,
  "expected_close_at": null,
  "created_at": "2026-10-23T14:05:00.118000Z",
  "updated_at": "2026-10-23T14:05:00.118000Z"
}

#Move an opportunity

POST /api/v1/crm-app/zapier/opportunities/{opportunity_id}/move

Moves an opportunity to another stage (by id or name), to another pipeline (its first stage unless a stage is given), and/or sets its status. Send at least one of stage, pipeline or status.

Parameters

NameTypeDescription
opportunity_id requiredstring pathThe opportunity's id (opp_…).
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmOpportunityMove

FieldTypeDescription
stage_idstringThe new stage's id or name.
stagestringThe new stage's id or name (any case).
pipeline_idstringMove to this pipeline (its first stage unless a stage is given).
pipelinestringThe pipeline's id or name.
statusstringOne of open, won, lost, abandoned.
lost_reasonstringWith status lost.

Responses

  • 200 The opportunity. Returns CrmOpportunity.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/opportunities/opp_7d8e9f0a1b2c3d4e5f6a/move" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "stage": "Proposal sent"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/opportunities/opp_7d8e9f0a1b2c3d4e5f6a/move",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "stage": "Proposal sent",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/opportunities/opp_7d8e9f0a1b2c3d4e5f6a/move", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "stage": "Proposal sent"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

#Create a task

POST /api/v1/crm-app/zapier/tasks

Adds a task, optionally on a contact (found like the other actions; 404 if given and not found).

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmTaskCreate

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
title requiredstringLonger titles are cut to 200 characters.
descriptionstring
opportunity_idstring
assigned_tostringA team member's user id.
due_atstringAn exact time. Wins over due_date.
due_datestringYYYY-MM-DD. Without due_time, due all day.
due_timestringHH:MM.
timezonestringThe IANA zone of due_date and due_time. Defaults to the assignee's zone, else the CRM's, else UTC.

Responses

  • 201 The task. Returns CrmTask.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 422 The body doesn't match the schema (a missing required field, a wrong type). detail lists the problems.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/tasks" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Call Jane",
    "email": "[email protected]",
    "due_date": "2026-10-27",
    "due_time": "10:00",
    "timezone": "America/Chicago"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/tasks",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "title": "Call Jane",
        "email": "[email protected]",
        "due_date": "2026-10-27",
        "due_time": "10:00",
        "timezone": "America/Chicago",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/tasks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "title": "Call Jane",
    "email": "[email protected]",
    "due_date": "2026-10-27",
    "due_time": "10:00",
    "timezone": "America/Chicago"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 201

json
{
  "id": "tsk_1e2f3a4b5c6d7e8f9a0b",
  "title": "Call Jane",
  "description_html": null,
  "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
  "opportunity_id": null,
  "assigned_to": null,
  "due_at": "2026-10-27T15:00:00Z",
  "due_date": "2026-10-27",
  "due_time": "10:00",
  "all_day": false,
  "timezone": "America/Chicago",
  "status": "open",
  "origin": {
    "kind": "manual",
    "reason": "Created by Zapier"
  },
  "created_at": "2026-10-23T14:08:00Z",
  "updated_at": "2026-10-23T14:08:00Z"
}

#Add a contact to a workflow

POST /api/v1/crm-app/zapier/workflows/{workflow_id}/enroll

Starts a published workflow for a contact. Not being enrolled is not an error: ok is false and reason says why (already_active, no_reentry, not_published).

Parameters

NameTypeDescription
workflow_id requiredstring pathThe workflow's id (wf_…). See GET /zapier/workflows.
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmContactLookup

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring

Responses

  • 200 The outcome. Returns CrmEnrollment.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/workflows/wf_0a1b2c3d4e5f6a7b8c9d/enroll" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]"
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/workflows/wf_0a1b2c3d4e5f6a7b8c9d/enroll",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "email": "[email protected]",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/workflows/wf_0a1b2c3d4e5f6a7b8c9d/enroll", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "[email protected]"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "ok": true,
  "reason": "enrolled",
  "enrollment_id": "wfe_6f7a8b9c0d1e2f3a4b5c",
  "workflow_id": "wf_0a1b2c3d4e5f6a7b8c9d",
  "workflow_name": "New lead follow-up",
  "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e"
}

#Send a text

POST /api/v1/crm-app/zapier/messages/sms

Texts a contact from the CRM's phone number. A marketing send: the contact needs explicit SMS consent, and DND, STOP opt-outs, quiet hours, daily limits and credits apply. A refused send is 422 with a reason; nothing is sent or queued. See Consent rules.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmTextSend

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
textstringRequired (or message). Cut at 1,600 characters; over 10 segments with the opt-out footer is refused (too_long).
messagestringSame as text.
mediaarray of stringOne image file already stored in SuperCool (its storage key), up to 1 MB, for MMS. Anything else is refused (media).

Responses

  • 201 Sent (see status). Returns CrmSent.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 422 The CRM's rules refused the send (detail is {reason, message}; see refusal reasons), or the body doesn't match the schema (detail is a list).
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/messages/sms" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": "+15555550123",
    "text": "Hi Jane, your order shipped today."
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/messages/sms",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "contact": "+15555550123",
        "text": "Hi Jane, your order shipped today.",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/messages/sms", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "contact": "+15555550123",
    "text": "Hi Jane, your order shipped today."
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 201

json
{
  "ok": true,
  "contact_id": "ct_8f2a6c1e9b3d5f7a0c4e",
  "id": "cmsg_9b2c4d6e8f0a1b3c5d7e",
  "status": "accepted",
  "kind": "sms",
  "conversation_id": "cv_3c4d5e6f7a8b9c0d1e2f",
  "segments": 1,
  "credits": 10,
  "error": null,
  "created_at": "2026-10-23T14:10:02.551000Z"
}

#Send an email

POST /api/v1/crm-app/zapier/messages/email

Emails a contact from the CRM's default sender. A marketing send: the contact needs explicit email consent, and DND, unsubscribes, bounces, daily limits and credits apply. A refused send is 422 with a reason; nothing is sent or queued. See Consent rules.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmEmailSend

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
subjectstringRequired. Cut at 300 characters.
htmlstringThe HTML body. html, body or text is required.
bodystringSame as html.
textstringA plain-text body. Without html, it also becomes the HTML body.

Responses

  • 201 Sent (see status). Returns CrmSent.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 404 The contact, opportunity, workflow or subscription isn't in this CRM.
  • 422 The CRM's rules refused the send (detail is {reason, message}; see refusal reasons), or the body doesn't match the schema (detail is a list).
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/zapier/messages/email" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "subject": "Your order shipped",
    "text": "Hi Jane,\nyour order shipped today."
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/zapier/messages/email",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "email": "[email protected]",
        "subject": "Your order shipped",
        "text": "Hi Jane,\nyour order shipped today.",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/messages/email", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "[email protected]",
    "subject": "Your order shipped",
    "text": "Hi Jane,\nyour order shipped today."
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

#List pipelines

GET /api/v1/crm-app/zapier/pipelines

The CRM's pipelines and their stages, in order.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The pipelines. Returns CrmPipelineList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/pipelines" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/pipelines",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/pipelines", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "pl_2b3c4d5e6f7a8b9c0d1e",
      "name": "Sales",
      "stages": [
        {
          "id": "st_4d5e6f7a8b9c0d1e2f3a",
          "name": "New lead",
          "position": 0
        },
        {
          "id": "st_5e6f7a8b9c0d1e2f3a4b",
          "name": "Qualified",
          "position": 1
        }
      ]
    }
  ]
}

#List workflows

GET /api/v1/crm-app/zapier/workflows

The CRM's published workflows.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The workflows. Returns CrmWorkflowList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.
  • 503 The feature isn't available on this account yet.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/workflows" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/workflows",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/workflows", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "wf_0a1b2c3d4e5f6a7b8c9d",
      "name": "New lead follow-up",
      "status": "published"
    }
  ]
}

#List tags

GET /api/v1/crm-app/zapier/tags

The CRM's tags.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The tags. Returns CrmTagList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/tags" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/tags",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/tags", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "tag_1a2b3c4d5e6f7a8b9c0d",
      "name": "lead"
    }
  ]
}

#List team members

GET /api/v1/crm-app/zapier/team

The team's members (in a personal CRM, just you). Their id is what owner_user_id and assigned_to take.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The members. Returns CrmTeamList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/team" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/team",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/team", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "id": "64f1c2a9e4b0a1b2c3d4e5f6",
      "name": "Sam Rivera",
      "email": "[email protected]",
      "role": "admin"
    }
  ]
}

#List contact custom fields

GET /api/v1/crm-app/zapier/fields

The contact custom fields. Their key is what fields takes.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Responses

  • 200 The fields. Returns CrmFieldList.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl "https://api.supercool.com/api/v1/crm-app/zapier/fields" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY"
Python
import os
import requests

resp = requests.get(
    "https://api.supercool.com/api/v1/crm-app/zapier/fields",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/zapier/fields", {
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 200

json
{
  "items": [
    {
      "key": "budget",
      "label": "Budget",
      "type": "number"
    }
  ]
}

#Post a lead

POST /api/v1/crm-app/leads

The Lead API. Creates the contact, or updates the one with the same email or phone, with source lead_api, plus optional attribution and an opportunity. Fires the form_submitted and lead_received triggers. Up to 600 leads per minute per key.

Parameters

NameTypeDescription
X-Workspace-Idstring headerThe team id, to use that team's CRM (the key's owner must be a member). Omit it for the key owner's personal CRM. Shown on the Zapier card in the CRM's Settings → Integrations.

Request body CrmLeadCreate

FieldTypeDescription
emailstring
phonestring
first_namestring
last_namestring
companystring
addressstring
citystring
statestring
postal_codestring
countrystring
websitestring
tagsarray of stringTag names to add (up to 50). Missing tags are created.
fieldsobjectCustom field values by key. Unknown keys are ignored.
consentbooleantrue records explicit email consent. Otherwise imported_unknown, unless already explicit.
sms_consentbooleantrue records explicit SMS consent, if the lead has a phone number.
sourcestringYour label for where the lead came from. Kept as lead_source.
attributionobjectutm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid, fbclid, referrer, landing_url. The first becomes the contact's first touch; each updates the last touch.
opportunityobjectAdd the lead to a pipeline: pipeline_id or pipeline (name), stage_id, value (minor units if a number, major if a string), name. If it can't be created, the lead is still saved.
workspace_idstringThe team id, if you don't send X-Workspace-Id.

Responses

  • 201 The lead's contact. Returns CrmLeadCreated.
  • 400 The input can't be used. detail says why.
  • 401 The API key is missing, unknown, expired or revoked.
  • 403 An isolated API key, or an X-Workspace-Id team the key's owner isn't a member of.
  • 422 The body doesn't match the schema (a missing required field, a wrong type). detail lists the problems.
  • 429 Over the key's per-minute limit (300 on /zapier/…, 600 on /leads). Wait and retry.

Example request

curl
curl -X POST "https://api.supercool.com/api/v1/crm-app/leads" \
  -H "Authorization: Bearer $SUPERCOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "first_name": "Jane",
    "phone": "+15555550123",
    "tags": [
      "webinar"
    ],
    "consent": true,
    "sms_consent": true,
    "source": "pricing page",
    "attribution": {
      "utm_source": "google",
      "utm_campaign": "fall-promo"
    },
    "opportunity": {
      "pipeline": "Sales",
      "value": 50000
    }
  }'
Python
import os
import requests

resp = requests.post(
    "https://api.supercool.com/api/v1/crm-app/leads",
    headers={"Authorization": f"Bearer {os.environ['SUPERCOOL_API_KEY']}"},
    json={
        "email": "[email protected]",
        "first_name": "Jane",
        "phone": "+15555550123",
        "tags": [
            "webinar",
        ],
        "consent": True,
        "sms_consent": True,
        "source": "pricing page",
        "attribution": {
            "utm_source": "google",
            "utm_campaign": "fall-promo",
        },
        "opportunity": {
            "pipeline": "Sales",
            "value": 50000,
        },
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json())
JavaScript
const res = await fetch("https://api.supercool.com/api/v1/crm-app/leads", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUPERCOOL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "[email protected]",
    "first_name": "Jane",
    "phone": "+15555550123",
    "tags": [
      "webinar"
    ],
    "consent": true,
    "sms_consent": true,
    "source": "pricing page",
    "attribution": {
      "utm_source": "google",
      "utm_campaign": "fall-promo"
    },
    "opportunity": {
      "pipeline": "Sales",
      "value": 50000
    }
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data.detail));
console.log(data);

Example response 201

json
{
  "ok": true,
  "created": true,
  "contact": {
    "id": "ct_8f2a6c1e9b3d5f7a0c4e",
    "email": "[email protected]",
    "phone": "+15555550123",
    "first_name": "Jane",
    "last_name": null,
    "display_name": "Jane",
    "tags": [
      "tag_9f8e7d6c5b4a39281706"
    ],
    "fields": {},
    "source": "lead_api",
    "status": "active"
  }
}

#Webhook events

What SuperCool POSTs to your endpoints. See Webhooks for signatures and retries.

#A job changed

Sent when one job (one piece of work) changes: job.completed, job.failed, job.blocked, job.resumed or job.expired. data is the job, with its message_id and files. Signed with the Standard Webhooks headers.

Headers

HeaderTypeDescription
webhook-id requiredstringThe delivery's id (evt_…). The same across retries of one event; use it to drop duplicates.
webhook-timestamp requiredstringUnix seconds when this attempt was signed.
webhook-signature requiredstringv1, + base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the base64-decoded part of your whsec_ secret.

Payload

FieldTypeDescription
typestringOne of job.completed, job.failed, job.blocked, job.resumed, job.expired.
timestampstring
dataobject
object"job"
message_idstring
job_idstringexe_… for work that ran or is queued; ref_… for work refused before it was queued.
kindstringOne of execution, refusal.
rolestringfollow_up when the message joined work that was already running; its status follows that work. One of execution, follow_up.
work_idstring | nullThe chat the work runs in.
titlestring | null
statusstring JobStatusOne of running, completed, failed, cancelled, blocked, expired.
reasonstring | nullFor blocked, failed, cancelled and expired: out_of_credits, hosting_fees, too_many_running, execution_failed, stopped, queue_expired, watch_expired, result_unknown.
finalbooleanfalse while running, and for queued work waiting for credits (it can still run).
queuedboolean | nulltrue for work that is queued and waiting for credits; otherwise null.
resume_bystring | nullFor queued work waiting for credits, the deadline to add them (24 hours after it was queued).
linkstring | nullThe work in the SuperCool app.
started_atstring | null
settled_atstring | null
filesarray of File

Example payload

json
{
  "type": "job.completed",
  "timestamp": "2026-09-30T14:06:40.310000Z",
  "data": {
    "object": "job",
    "message_id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d",
    "job_id": "exe_9b2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e",
    "kind": "execution",
    "role": "execution",
    "work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
    "title": "Candle shop vertical ad",
    "status": "completed",
    "reason": null,
    "final": true,
    "queued": null,
    "resume_by": null,
    "link": "https://supercool.com/chat/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
    "started_at": "2026-09-30T14:02:11.402000Z",
    "settled_at": "2026-09-30T14:06:40.221000Z",
    "files": []
  }
}

#A message reached a status

Sent once each time a message reaches a status other than processing: message.completed, message.partial, message.failed, message.needs_input, message.blocked or message.expired. data is the full message, exactly as GET /messages/{message_id} returns it.

Headers

HeaderTypeDescription
webhook-id requiredstringThe delivery's id (evt_…). The same across retries of one event; use it to drop duplicates.
webhook-timestamp requiredstringUnix seconds when this attempt was signed.
webhook-signature requiredstringv1, + base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the base64-decoded part of your whsec_ secret.

Payload

FieldTypeDescription
typestringOne of message.completed, message.partial, message.failed, message.needs_input, message.blocked, message.expired.
timestampstring
dataMessageA message you sent and everything that came of it.

#Objects

#MessageCreate

A message to your agent. Send message, files, or both.

FieldTypeDescription
messagestringWhat you want, in plain language. Up to 20,000 characters; send longer text as a file.
filesarray of InlineFileSmall files inline (up to 5, 10 MB each, 25 MB in total). Each has a url or base64. A file that can't be fetched doesn't fail the message: it's listed in the message's notes.
upload_idsarray of stringFinished uploads to attach (up to 10, 1 GB in total). See POST /uploads.
webhook_endpoint_idstring | nullSend this message's webhook events to this registered endpoint (whe_…) instead of the account's endpoints.

#InlineFile

FieldTypeDescription
namestring | nullA file name for the agent to see.
urlstring | nullA public https URL to fetch the file from.
base64string | nullThe file's bytes, base64-encoded.
content_typestring | nullThe MIME type, for base64 files.

#Message

A message you sent and everything that came of it.

FieldTypeDescription
id requiredstringThe message id (msg_…), always generated by SuperCool.
object required"message"
status requiredstring MessageStatusOne of processing, needs_input, blocked, completed, partial, failed, expired.
reasonstring | nullWhy, for blocked, failed, partial and expired: out_of_credits, hosting_fees, too_many_running, queue_expired, watch_expired, result_unknown, execution_failed, stopped, agent_busy, turn_failed and others. See Getting results.
questionstring | nullFor needs_input, the agent's question. Answer by sending a new message.
final requiredbooleantrue when nothing about this message will change any more. Stop polling.
replystring | nullWhat the agent said, once it has answered.
jobs requiredarray of JobThe pieces of work this message started, each with its own status.
files requiredarray of FileEvery file the message produced (handed over in the reply, or made by its jobs).
notes requiredarray of stringProblems with attachments (a file that couldn't be fetched, files over the limit).
credits_used requirednumberCredits this message's jobs used so far, in API units (hundredths of the figure the app shows; see Units).
created_at requiredstring
updated_at requiredstringWhen the agent's turn ended (or created_at while it's still going).
resume"add_credits"Present when queued work is waiting for credits: add credits before resume_by and it runs by itself. Don't resend the message.
resume_bystringThe earliest deadline among queued jobs waiting for credits (24 hours after they were queued).
retry_after_secondsintegerWith reason: "agent_busy": retry with the same Idempotency-Key after this many seconds.
runningarray of objectPresent when the agent tried to start work while 5 jobs were already running on the account (a job with reason: "too_many_running"): the work running now, and the message that started each.
job_idstringexe_…
message_idstring
work_idstring

#MessageStatus

The message's status, aggregated over its jobs.

string One of processing, needs_input, blocked, completed, partial, failed, expired.

#Job

One piece of work a message started (or was refused).

FieldTypeDescription
job_idstringexe_… for work that ran or is queued; ref_… for work refused before it was queued.
kindstringOne of execution, refusal.
rolestringfollow_up when the message joined work that was already running; its status follows that work. One of execution, follow_up.
work_idstring | nullThe chat the work runs in.
titlestring | null
statusstring JobStatusOne of running, completed, failed, cancelled, blocked, expired.
reasonstring | nullFor blocked, failed, cancelled and expired: out_of_credits, hosting_fees, too_many_running, execution_failed, stopped, queue_expired, watch_expired, result_unknown.
finalbooleanfalse while running, and for queued work waiting for credits (it can still run).
queuedboolean | nulltrue for work that is queued and waiting for credits; otherwise null.
resume_bystring | nullFor queued work waiting for credits, the deadline to add them (24 hours after it was queued).
linkstring | nullThe work in the SuperCool app.
started_atstring | null
settled_atstring | null
filesarray of File

#JobStatus

string One of running, completed, failed, cancelled, blocked, expired.

#File

A file the agent made or handed over.

FieldTypeDescription
file_idstring<work_id>/<path>. Use it with GET /files/{file_id} for a fresh link.
namestring
content_typestring | null
kindstringOne of image, video, audio, doc, site.
sizeinteger | nullBytes. null for a site.
urlstring | nullA signed download link. It expires, and it is pinned to revision; a download answers 412 if the file changed since.
revisionstring | nullThe file's version the link is pinned to. null for a site (its link always opens the live site).
work_idstring | null
labelstring | null

#FileObject

A file the agent made or handed over.

FieldTypeDescription
object"file"
file_idstring<work_id>/<path>. Use it with GET /files/{file_id} for a fresh link.
namestring
content_typestring | null
kindstringOne of image, video, audio, doc, site.
sizeinteger | nullBytes. null for a site.
urlstring | nullA signed download link. It expires, and it is pinned to revision; a download answers 412 if the file changed since.
revisionstring | nullThe file's version the link is pinned to. null for a site (its link always opens the live site).
work_idstring | null
labelstring | null

#EventList

FieldTypeDescription
object"list"
dataarray of Event
has_morebooleanMore entries are ready; call again with cursor right away.
donebooleanThe message has nothing more to report.
cursorstringPass this back as cursor for the next page.
cursor_expiredbooleanYour cursor pointed at entries older than 7 days. cursor restarts from now.

#Event

One entry in a message's update log. Fields beyond the common ones depend on type.

FieldTypeDescription
seqinteger
typestringresult (a job finished, with text and files), progress (a step started or a file landed), agent_reply (the agent's reply), state (resumed, stopped, expired, unknown), turn_failed, turn_busy. One of result, progress, agent_reply, state, turn_failed, turn_busy.
work_idstring | null
message_idstring | nullThe message the entry belongs to, when known.
atstring
job_idstringThe job (exe_…) the entry belongs to, when known.
titlestring | nullresult
textstring | nullresult, agent_reply, state, turn_failed, turn_busy
outcomestringresult One of completed, failed, stalled_credits.
filesarray of Fileresult, agent_reply
statestringstate
stepstring | nullprogress
statusstring | nullprogress
file_namesarray of stringprogress: names of files that just landed (download them from the result).
delivered_syncboolean | nullagent_reply: true when this reply was already in the POST /messages response.
reasonstring | nullturn_failed, turn_busy

#Recovery

FieldTypeDescription
idstring
object"recovery"
workarray of object
work_idstring
statestringOne of settled, watching, recovering, exhausted.
outcomestringFor settled, how it ended.

#UploadCreate

FieldTypeDescription
filename requiredstring
size requiredintegerThe exact size in bytes (up to 500 MB). The upload is capped at this size.
content_typestring | nullThe MIME type. Guessed from the file name when omitted. The POST must send the same Content-Type field (it is in fields).

#UploadCreated

FieldTypeDescription
object"upload"
upload_idstring
urlstringWhere to POST the file (multipart/form-data).
fieldsobjectForm fields to send with the file, exactly as given.
expires_atstringThe presigned POST works until then (15 minutes).
max_bytesinteger

#Upload

FieldTypeDescription
object"upload"
upload_idstring
statusstringOne of ready.
filenamestring
content_typestring
sizeinteger

#WorkList

FieldTypeDescription
object"list"
dataarray of WorkSummary

#WorkSummary

FieldTypeDescription
object"work"
work_idstring
titlestring | null
previewstring | null
linkstring

#Work

FieldTypeDescription
object"work"
work_idstring
titlestring | null
statusstringrunning, stalled_credits (stopped until credits are added), or idle. One of running, stalled_credits, idle.
textstringThe latest result text, from from_char.
next_from_charinteger | nullPass as from_char to read on; null when there's no more.
linkstring
filesarray of FileThe most recent files (up to 8).

#Account

FieldTypeDescription
object"account"
user_idstring
emailstring | null
namestring | null
planstring | null
creditsnumber | nullCredits available now, in API units (hundredths of the figure the app shows; see Units).
agentobject
namestring
keyobject
idstring
namestring
limitsobject
running_jobsintegerRunning jobs allowed per account (5).
requests_per_minuteintegerRequests allowed per API key per minute (600).

#WebhookEventType

string One of job.completed, job.failed, job.blocked, job.resumed, job.expired, message.completed, message.partial, message.failed, message.needs_input, message.blocked, message.expired.

#WebhookCreate

FieldTypeDescription
url requiredstringA public https URL (port 443 or 8443, up to 2,000 characters).
eventsarray of string | nullEvents to receive, or ["*"] for all (the default).
descriptionstring | nullUp to 120 characters.

#WebhookUpdate

FieldTypeDescription
urlstring | null
eventsarray of string | null
descriptionstring | null
enabledboolean | nulltrue turns the endpoint back on and clears its failure state; false turns it off.

#WebhookEndpoint

FieldTypeDescription
object"webhook_endpoint"
idstringwhe_…
urlstring
eventsarray of string
descriptionstring | null
created_atstring
disabled_atstring | nullSet when the endpoint is off (by you, or after 3 days of failed deliveries).
disabled_reasonstring | null
failing_sincestring | nullWhen deliveries started failing. Cleared by the next success.

#WebhookEndpointWithSecret

FieldTypeDescription
object"webhook_endpoint"
idstringwhe_…
urlstring
eventsarray of string
descriptionstring | null
created_atstring
disabled_atstring | nullSet when the endpoint is off (by you, or after 3 days of failed deliveries).
disabled_reasonstring | null
failing_sincestring | nullWhen deliveries started failing. Cleared by the next success.
secretstringThe signing secret (whsec_…). Shown only in this response.

#WebhookEndpointDetail

FieldTypeDescription
object"webhook_endpoint"
idstringwhe_…
urlstring
eventsarray of string
descriptionstring | null
created_atstring
disabled_atstring | nullSet when the endpoint is off (by you, or after 3 days of failed deliveries).
disabled_reasonstring | null
failing_sincestring | nullWhen deliveries started failing. Cleared by the next success.
recent_deliveriesarray of WebhookDelivery

#WebhookEndpointList

FieldTypeDescription
object"list"
dataarray of WebhookEndpoint

#WebhookDelivery

FieldTypeDescription
object"webhook_delivery"
idstringThe delivery id (evt_…), sent as webhook-id.
eventstring WebhookEventTypeOne of job.completed, job.failed, job.blocked, job.resumed, job.expired, message.completed, message.partial, message.failed, message.needs_input, message.blocked, message.expired.
statusstringOne of pending, delivered, cancelled, dead.
attemptsinteger
last_status_codeinteger | null
last_errorstring | null
last_responsestring | nullThe first 4 KB of your endpoint's last response.
last_msinteger | null
created_atstring
delivered_atstring | null
next_attempt_atstring | null
message_idstring | null

#RotatedSecret

FieldTypeDescription
object"webhook_endpoint"
idstring
secretstring

#Deleted

FieldTypeDescription
object"webhook_endpoint"
idstring
deletedtrue

#JobEvent

FieldTypeDescription
typestringOne of job.completed, job.failed, job.blocked, job.resumed, job.expired.
timestampstring
dataobject
object"job"
message_idstring
job_idstringexe_… for work that ran or is queued; ref_… for work refused before it was queued.
kindstringOne of execution, refusal.
rolestringfollow_up when the message joined work that was already running; its status follows that work. One of execution, follow_up.
work_idstring | nullThe chat the work runs in.
titlestring | null
statusstring JobStatusOne of running, completed, failed, cancelled, blocked, expired.
reasonstring | nullFor blocked, failed, cancelled and expired: out_of_credits, hosting_fees, too_many_running, execution_failed, stopped, queue_expired, watch_expired, result_unknown.
finalbooleanfalse while running, and for queued work waiting for credits (it can still run).
queuedboolean | nulltrue for work that is queued and waiting for credits; otherwise null.
resume_bystring | nullFor queued work waiting for credits, the deadline to add them (24 hours after it was queued).
linkstring | nullThe work in the SuperCool app.
started_atstring | null
settled_atstring | null
filesarray of File

#MessageEvent

FieldTypeDescription
typestringOne of message.completed, message.partial, message.failed, message.needs_input, message.blocked, message.expired.
timestampstring
dataMessageA message you sent and everything that came of it.

#Error

Every API error has this shape.

FieldTypeDescription
error requiredstring ErrorCodeOne of bad_request, invalid_request, empty_message, too_long, bad_cursor, bad_url, bad_events, bad_size, too_many, unauthorized, not_found, idempotency_conflict, poll_in_progress, too_many_endpoints, not_pending, not_uploaded, changed, upload_not_ready, too_large, executable_refused, rate_limited, internal, unavailable.
message requiredstringWhat went wrong, for people.
request_id requiredstringThe request's id (req_…), also in the Request-Id header.
docs requiredstringA link to this error code's documentation.
problemsarray of objectWith invalid_request: what doesn't match, one entry per problem (up to 10).
fieldstring | nullThe dotted location of the field (like files.0.url or wait), or null for the body as a whole.
problemstring
cursorstringWith poll_in_progress: the cursor to use when you call again.

#ErrorCode

A stable, machine-readable error code. Each one is documented at https://supercool.com/docs/api/errors#<code>.

string One of bad_request, invalid_request, empty_message, too_long, bad_cursor, bad_url, bad_events, bad_size, too_many, unauthorized, not_found, idempotency_conflict, poll_in_progress, too_many_endpoints, not_pending, not_uploaded, changed, upload_not_ready, too_large, executable_refused, rate_limited, internal, unavailable.

#CrmError

A CRM API error: FastAPI's {"detail": …}.

FieldTypeDescription
detail requiredstringWhat went wrong, for people.

#CrmValidationError

A body that doesn't match the endpoint's schema (422).

FieldTypeDescription
detail requiredarray of objectOne entry per problem.
typestringThe kind of problem, like missing.
locarray of string | integerWhere, like ["body", "title"].
msgstringThe problem, for people.
inputanyThe value that was sent.

#CrmRefusal

A text or email the CRM's rules refused (422). Nothing was sent or queued.

FieldTypeDescription
detail requiredobject
reason requiredstringMachine-readable: no_consent, dnd, suppressed_owner, suppressed_global, paused, cap, credits, no_phone, landline, quiet_hours, no_number, unregistered, suspended, releasing, unpaid, kill_switch, too_long, media, no_email, no_channel, unsubscribed, bounced. See refusal reasons.
message requiredstringWhat happened, for people.

#CrmOk

FieldTypeDescription
ok requiredtrue

#CrmAccount

An API key, its owner, and the CRM it reaches.

FieldTypeDescription
ok requiredtrue
user_id requiredstringThe key owner's user id.
emailstring | nullThe key owner's email address.
workspace_id requiredstring | nullThe team id, or null for the owner's personal CRM.
team_namestring | nullThe team's name, or null for a personal CRM.
role requiredstringThe key owner's role in this CRM. One of admin, member.
key requiredobject
idstringThe key's id.
namestring | nullThe key's nickname.
hintstring | nullIts first and last characters, like sc_key_9fK2…x7Qa.
label requiredstringA name for the connection, like [email protected] · Rivera Studio.

#CrmTrigger

FieldTypeDescription
keystringThe trigger key to subscribe with, like new_contact.
labelstringIts name, like New Contact.
nounstringWhat it's about, like Contact.
typesarray of stringThe CRM event types it fires on, like contact.created.

#CrmTriggerList

FieldTypeDescription
items requiredarray of CrmTrigger

#CrmHookCreate

FieldTypeDescription
event requiredstringA trigger key (GET /zapier/triggers), like new_contact.
target_url requiredstringA public http or https URL. Private and local addresses are refused. Up to 2048 characters.

#CrmHookDelete

Send id, or target_url (optionally with event).

FieldTypeDescription
idstringThe subscription's id (zh_…).
target_urlstringRemove every subscription to this URL.
eventstringWith target_url, only the subscription for this trigger.

#CrmHook

A REST hook subscription. It belongs to the CRM, not to the key.

FieldTypeDescription
idstringzh_…
key_idstringThe API key that created it. If that key is revoked, the subscription is removed at its next event.
eventstringThe trigger key.
labelstringThe trigger's name.
target_urlstring
target_hoststring | null
key_namestring | nullThe creating key's nickname.
key_hintstring | nullThe creating key's first and last characters.
deliveriesintegerEvents delivered.
failuresintegerEvents that failed for good.
last_delivery_atstring | null
last_statusinteger | nullThe HTTP status of the latest attempt.
last_errorstring | nullThe latest attempt's error, if it failed.
created_atstring
updated_atstring

#CrmHookList

FieldTypeDescription
items requiredarray of CrmHook

#CrmEvent

What a REST hook receives, and what GET /zapier/perform-list/{event} returns: one CRM event and its contact. POSTed with X-SuperCool-Event and X-SuperCool-Delivery headers; see Delivery.

FieldTypeDescription
idstringThe event id (ev_…). The same on every retry; use it to drop duplicates.
triggerstringThe trigger key the subscription is for.
typestringThe CRM event type, like contact.created.
labelstringThe event type's name, like Contact created.
occurred_atstring
contact_idstring | null
contactobject | nullThe contact, flattened for field mapping.
idstring
emailstring | null
phonestring | null
first_namestring | null
last_namestring | null
full_namestring | null
companystring | null
addressstring | null
citystring | null
statestring | null
postal_codestring | null
countrystring | null
websitestring | null
sourcestring | null
owner_user_idstring | null
fieldsobjectCustom field values by key.
tag_idsarray of string
tagsarray of stringTag names.
dndobjectemail, sms, calls, all (booleans), reason, at.
consentobjectemail and sms: each consent status, like explicit, or null.
created_atstring
updated_atstring
dataobjectWhat happened. The fields depend on the trigger; see the trigger catalogue.
refobjectIds of related records, like opportunity_id, appointment_id, task_id or form_id.
actorobjectWho did it: kind (user, api_key, webhook or system) and id.

#CrmEventList

FieldTypeDescription
items requiredarray of CrmEvent

#CrmContactLookup

Names an existing contact: by contact_id (or contact) as an id, then as an email address or phone number, then by email, then by phone. No match is 404 Contact not found.

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring

#CrmTags

Tag names, as a list or one comma-separated string (up to 50, 80 characters each).

array of string or string

#CrmContactWrite

A contact's fields. For create or update, email or phone is required and finds the contact. For update, contact_id or contact (or email / phone) finds it.

FieldTypeDescription
contact_idstringUpdate only. The contact's id.
contactstringUpdate only. An id, email address or phone number.
emailstring
phonestringStored in E.164. US numbers without a country code get +1.
first_namestring
last_namestring
companystring
addressstring
citystring
statestring
postal_codestring
countrystring
websitestring
owner_user_idstringA team member's user id (GET /zapier/team).
tagsarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).
fieldsobjectCustom field values by key (GET /zapier/fields). Create or update ignores unknown keys; update refuses them.
consentbooleanCreate or update only. true records explicit email consent. Otherwise email consent is imported_unknown, unless already explicit.
sms_consentbooleanCreate or update only. true records explicit SMS consent, if the contact has a phone number.

#CrmContact

A CRM contact. Other fields may be present, like attribution.

FieldTypeDescription
idstringct_…
emailstring | null
phonestring | nullE.164.
first_namestring | null
last_namestring | null
companystring | null
addressstring | null
citystring | null
statestring | null
postal_codestring | null
countrystring | null
websitestring | null
display_namestringThe full name, else the email, else the phone.
tagsarray of stringTag ids (tag_…).
fieldsobjectCustom field values by key.
sourcestringWhere it came from, like zapier, lead_api, form, import or manual.
owner_user_idstring | null
statusstringOne of active, unsubscribed, bounced, blocked.
dndobjectDo Not Disturb: email, sms, calls, all (booleans), reason, at.
consentobjectemail and call, each {status, source, at}. Send Email needs email consent explicit.
last_activity_atstring
created_atstring
updated_atstring

#CrmContactResult

The contact's fields at the top level, plus ok (and created from create or update).

FieldTypeDescription
oktrue
createdbooleanCreate or update only. true if the contact is new.
idstringct_…
emailstring | null
phonestring | nullE.164.
first_namestring | null
last_namestring | null
companystring | null
addressstring | null
citystring | null
statestring | null
postal_codestring | null
countrystring | null
websitestring | null
display_namestringThe full name, else the email, else the phone.
tagsarray of stringTag ids (tag_…).
fieldsobjectCustom field values by key.
sourcestringWhere it came from, like zapier, lead_api, form, import or manual.
owner_user_idstring | null
statusstringOne of active, unsubscribed, bounced, blocked.
dndobjectDo Not Disturb: email, sms, calls, all (booleans), reason, at.
consentobjectemail and call, each {status, source, at}. Send Email needs email consent explicit.
last_activity_atstring
created_atstring
updated_atstring

#CrmContactList

FieldTypeDescription
items requiredarray of CrmContactThe contact, or empty.

#CrmTagsChange

A contact (see CrmContactLookup) and the tags to add or remove.

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
tags requiredarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).

#CrmOpportunityCreate

A contact (see CrmContactLookup) and the opportunity.

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
pipeline_idstringThe pipeline's id (pl_…). With neither this nor pipeline, the default pipeline.
pipelinestringThe pipeline's id or name.
stage_idstringThe stage's id (st_…) or name. With neither this nor stage, the first stage.
stagestringThe stage's id or name (any case).
namestringDefaults to the contact's name.
valuenumber or stringA number is in minor units (150000 is 1,500.00); a string is in major units ("1,500.00").
currencystringA 3-letter code. Defaults to the CRM's currency.
owner_user_idstringA team member's user id. Ignored if they're not on the team.
tagsarray of string or string CrmTagsTag names, as a list or one comma-separated string (up to 50, 80 characters each).
fieldsobjectOpportunity custom field values by key.
expected_close_atstringYYYY-MM-DD.
allow_duplicatebooleanAllow a second open opportunity for the contact in this pipeline.

#CrmOpportunityMove

At least one of a stage, a pipeline or a status.

FieldTypeDescription
stage_idstringThe new stage's id or name.
stagestringThe new stage's id or name (any case).
pipeline_idstringMove to this pipeline (its first stage unless a stage is given).
pipelinestringThe pipeline's id or name.
statusstringOne of open, won, lost, abandoned.
lost_reasonstringWith status lost.

#CrmOpportunity

A potential sale on one contact, in one stage of one pipeline. Other fields are included too, like followers, stage_entered_at and days_in_stage.

FieldTypeDescription
idstringopp_…
pipeline_idstring
stage_idstring
contact_idstring
namestring
statusstringOne of open, won, lost, abandoned.
valueintegerIn minor units (cents).
currencystringLower-case 3-letter code.
sourcestring | null
owner_user_idstring | null
tagsarray of stringTag ids.
fieldsobject
lost_reasonstring | null
expected_close_atstring | null
created_atstring
updated_atstring

#CrmTaskCreate

The task, and optionally its contact (see CrmContactLookup).

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
title requiredstringLonger titles are cut to 200 characters.
descriptionstring
opportunity_idstring
assigned_tostringA team member's user id.
due_atstringAn exact time. Wins over due_date.
due_datestringYYYY-MM-DD. Without due_time, due all day.
due_timestringHH:MM.
timezonestringThe IANA zone of due_date and due_time. Defaults to the assignee's zone, else the CRM's, else UTC.

#CrmTask

FieldTypeDescription
idstringtsk_…
titlestring
description_htmlstring | null
contact_idstring | null
opportunity_idstring | null
assigned_tostring | null
due_atstring | null
due_datestring | null
due_timestring | null
all_dayboolean
timezonestring | null
statusstringopen for a new task.
originobject{"kind": "manual", "reason": "Created by Zapier"} for tasks this API creates.
created_atstring
updated_atstring

#CrmEnrollment

FieldTypeDescription
okbooleantrue if the contact was enrolled.
reasonstringOne of enrolled, already_active, no_reentry, not_published.
enrollment_idstring | nullwfe_…, when enrolled.
workflow_idstring
workflow_namestring | null
contact_idstring

#CrmTextSend

A contact (see CrmContactLookup) and the text.

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
textstringRequired (or message). Cut at 1,600 characters; over 10 segments with the opt-out footer is refused (too_long).
messagestringSame as text.
mediaarray of stringOne image file already stored in SuperCool (its storage key), up to 1 MB, for MMS. Anything else is refused (media).

#CrmEmailSend

A contact (see CrmContactLookup) and the email.

FieldTypeDescription
contact_idstringThe contact's id (ct_…).
contactstringAn id, email address or phone number.
emailstring
phonestring
subjectstringRequired. Cut at 300 characters.
htmlstringThe HTML body. html, body or text is required.
bodystringSame as html.
textstringA plain-text body. Without html, it also becomes the HTML body.

#CrmSent

A text or email that was sent, or handed to the carrier or provider.

FieldTypeDescription
oktrue
contact_idstring
idstringThe message id (cmsg_…).
statusstringaccepted, uncertain or failed; a text can also be refused (the carrier says the number opted out).
kindstringsms or email.
conversation_idstring
segmentsintegerTexts only.
creditsintegerWhat the send cost.
errorstring | null
created_atstring

#CrmPipelineList

FieldTypeDescription
items requiredarray of object
idstringpl_…
namestring
stagesarray of objectIn order.
idstringst_…
namestring
positioninteger

#CrmWorkflowList

FieldTypeDescription
items requiredarray of objectPublished workflows.
idstringwf_…
namestring
statusstring

#CrmTagList

FieldTypeDescription
items requiredarray of object
idstringtag_…
namestring

#CrmTeamList

FieldTypeDescription
items requiredarray of object
idstringThe member's user id.
namestring | null
emailstring | null
rolestringOne of admin, member.

#CrmFieldList

FieldTypeDescription
items requiredarray of object
keystringWhat fields takes.
labelstring
typestringOne of text, number, date, select, multiselect, checkbox, url.

#CrmLeadCreate

A lead. email or phone is required.

FieldTypeDescription
emailstring
phonestring
first_namestring
last_namestring
companystring
addressstring
citystring
statestring
postal_codestring
countrystring
websitestring
tagsarray of stringTag names to add (up to 50). Missing tags are created.
fieldsobjectCustom field values by key. Unknown keys are ignored.
consentbooleantrue records explicit email consent. Otherwise imported_unknown, unless already explicit.
sms_consentbooleantrue records explicit SMS consent, if the lead has a phone number.
sourcestringYour label for where the lead came from. Kept as lead_source.
attributionobjectutm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid, fbclid, referrer, landing_url. The first becomes the contact's first touch; each updates the last touch.
opportunityobjectAdd the lead to a pipeline: pipeline_id or pipeline (name), stage_id, value (minor units if a number, major if a string), name. If it can't be created, the lead is still saved.
workspace_idstringThe team id, if you don't send X-Workspace-Id.

#CrmLeadCreated

FieldTypeDescription
oktrue
createdbooleantrue if the contact is new.
contactCrmContactA CRM contact. Other fields may be present, like attribution.

Last updated