Reference
API reference
Every endpoint of the SuperCool API: parameters, request and response schemas, errors, and curl, Python and JavaScript samples.
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
Files
Account
#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
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string header | Any 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
| Field | Type | Description |
|---|---|---|
message | string | What you want, in plain language. Up to 20,000 characters; send longer text as a file. |
files | array of InlineFile | Small 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_ids | array of string | Finished uploads to attach (up to 10, 1 GB in total). See POST /uploads. |
webhook_endpoint_id | string | null | Send this message's webhook events to this registered endpoint (whe_…) instead of the account's endpoints. |
Responses
- 200 An
Idempotency-Keyyou 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(withproblems) 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-Afterseconds.
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 -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"
}'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())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
| Name | Type | Description |
|---|---|---|
message_id required | string path | The message's id (msg_…). |
wait | integer query | Long-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(withproblems) 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-Afterseconds.
Errors
400 invalid_request 401 unauthorized 404 not_found 429 rate_limited
Example request
curl "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d?wait=30" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
message_id required | string path | The message's id (msg_…). |
cursor | string query | The cursor from the previous page. Opaque. |
wait | integer query | Wait 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(withproblems) 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-Afterseconds.
Errors
400 invalid_request 400 bad_cursor 401 unauthorized 404 not_found 409 poll_in_progress 429 rate_limited
Example request
curl "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/events?wait=30" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
message_id required | string path | The 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-Afterseconds.
Errors
401 unauthorized 404 not_found 429 rate_limited
Example request
curl -X POST "https://api.supercool.com/v1/messages/msg_3f9a1c2e7b8d4e5f6a7b8c9d/recover" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"id": "msg_3f9a1c2e7b8d4e5f6a7b8c9d",
"object": "recovery",
"work": [
{
"work_id": "5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f",
"state": "recovering"
}
]
}#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
| Field | Type | Description |
|---|---|---|
filename required | string | |
size required | integer | The exact size in bytes (up to 500 MB). The upload is capped at this size. |
content_type | string | null | The 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
urlnext. Returns UploadCreated. - 400 The request can't be processed as sent.
invalid_request(withproblems) 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-Afterseconds.
Errors
400 invalid_request 400 bad_size 401 unauthorized 413 too_large 429 rate_limited
Example request
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"
}'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())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
{
"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
| Name | Type | Description |
|---|---|---|
upload_id required | string path | The 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-Afterseconds.
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 -X POST "https://api.supercool.com/v1/uploads/up_0a1b2c3d4e5f60718293a4b5c6d7e8f9/complete" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
limit | integer query | How 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(withproblems) when it doesn't match the schema. - 401 The API key is missing, unknown or revoked.
- 429 A rate limit was hit. Wait
Retry-Afterseconds.
Errors
400 invalid_request 401 unauthorized 429 rate_limited
Example request
curl "https://api.supercool.com/v1/work?limit=10" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
work_id required | string path | The work_id from a job, an event or GET /work. |
from_char | integer query | Read 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(withproblems) 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-Afterseconds.
Errors
400 invalid_request 401 unauthorized 404 not_found 429 rate_limited
Example request
curl "https://api.supercool.com/v1/work/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
file_id required | string path | The 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-Afterseconds.
Errors
401 unauthorized 404 not_found 429 rate_limited
Example request
curl "https://api.supercool.com/v1/files/5d0c3e2a-8f1b-4c6d-9e7f-1a2b3c4d5e6f/candle_ad.mp4" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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-Afterseconds.
Errors
401 unauthorized 429 rate_limited
Example request
curl "https://api.supercool.com/v1/me" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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-Afterseconds.
Errors
401 unauthorized 429 rate_limited
Example request
curl "https://api.supercool.com/v1/webhooks" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Field | Type | Description |
|---|---|---|
url required | string | A public https URL (port 443 or 8443, up to 2,000 characters). |
events | array of string | null | Events to receive, or ["*"] for all (the default). |
description | string | null | Up 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(withproblems) 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-Afterseconds.
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 -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"
}'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())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
{
"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
| Name | Type | Description |
|---|---|---|
endpoint_id required | string path | The 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-Afterseconds.
Errors
401 unauthorized 404 not_found 429 rate_limited
Example request
curl "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
endpoint_id required | string path | The webhook endpoint's id (whe_…). |
Request body WebhookUpdate
| Field | Type | Description |
|---|---|---|
url | string | null | |
events | array of string | null | |
description | string | null | |
enabled | boolean | null | true 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(withproblems) 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-Afterseconds.
Errors
400 invalid_request 400 bad_url 400 bad_events 400 bad_request 401 unauthorized 404 not_found 429 rate_limited
Example request
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
}'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())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
| Name | Type | Description |
|---|---|---|
endpoint_id required | string path | The 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-Afterseconds.
Errors
401 unauthorized 404 not_found 429 rate_limited
Example request
curl -X DELETE "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"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
| Name | Type | Description |
|---|---|---|
endpoint_id required | string path | The 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-Afterseconds.
Errors
401 unauthorized 404 not_found 429 rate_limited
Example request
curl -X POST "https://api.supercool.com/v1/webhooks/whe_4c1d2e3f4a5b6c7d8e9f/rotate-secret" \
-H "Authorization: Bearer $SUPERCOOL_API_KEY"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())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
{
"object": "webhook_endpoint",
"id": "whe_4c1d2e3f4a5b6c7d8e9f",
"secret": "whsec_Q2hhbmdlZC1zZWNyZXQtZXhhbXBsZTEy"
}#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
| Header | Type | Description |
|---|---|---|
webhook-id required | string | The delivery's id (evt_…). The same across retries of one event; use it to drop duplicates. |
webhook-timestamp required | string | Unix seconds when this attempt was signed. |
webhook-signature required | string | v1, + base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the base64-decoded part of your whsec_ secret. |
Payload
| Field | Type | Description |
|---|---|---|
type | string | One of job.completed, job.failed, job.blocked, job.resumed, job.expired. |
timestamp | string | |
data | object | |
object | "job" | |
message_id | string | |
job_id | string | exe_… for work that ran or is queued; ref_… for work refused before it was queued. |
kind | string | One of execution, refusal. |
role | string | follow_up when the message joined work that was already running; its status follows that work. One of execution, follow_up. |
work_id | string | null | The chat the work runs in. |
title | string | null | |
status | string JobStatus | One of running, completed, failed, cancelled, blocked, expired. |
reason | string | null | For blocked, failed, cancelled and expired: out_of_credits, hosting_fees, too_many_running, execution_failed, stopped, queue_expired, watch_expired, result_unknown. |
final | boolean | false while running, and for queued work waiting for credits (it can still run). |
queued | boolean | null | true for work that is queued and waiting for credits; otherwise null. |
resume_by | string | null | For queued work waiting for credits, the deadline to add them (24 hours after it was queued). |
link | string | null | The work in the SuperCool app. |
started_at | string | null | |
settled_at | string | null | |
files | array of File |
Example payload
{
"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
| Header | Type | Description |
|---|---|---|
webhook-id required | string | The delivery's id (evt_…). The same across retries of one event; use it to drop duplicates. |
webhook-timestamp required | string | Unix seconds when this attempt was signed. |
webhook-signature required | string | v1, + base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the base64-decoded part of your whsec_ secret. |
Payload
| Field | Type | Description |
|---|---|---|
type | string | One of message.completed, message.partial, message.failed, message.needs_input, message.blocked, message.expired. |
timestamp | string | |
data | Message | A message you sent and everything that came of it. |
#Objects
#MessageCreate
A message to your agent. Send message, files, or both.
| Field | Type | Description |
|---|---|---|
message | string | What you want, in plain language. Up to 20,000 characters; send longer text as a file. |
files | array of InlineFile | Small 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_ids | array of string | Finished uploads to attach (up to 10, 1 GB in total). See POST /uploads. |
webhook_endpoint_id | string | null | Send this message's webhook events to this registered endpoint (whe_…) instead of the account's endpoints. |
#InlineFile
| Field | Type | Description |
|---|---|---|
name | string | null | A file name for the agent to see. |
url | string | null | A public https URL to fetch the file from. |
base64 | string | null | The file's bytes, base64-encoded. |
content_type | string | null | The MIME type, for base64 files. |
#Message
A message you sent and everything that came of it.
| Field | Type | Description |
|---|---|---|
id required | string | The message id (msg_…), always generated by SuperCool. |
object required | "message" | |
status required | string MessageStatus | One of processing, needs_input, blocked, completed, partial, failed, expired. |
reason | string | null | Why, 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. |
question | string | null | For needs_input, the agent's question. Answer by sending a new message. |
final required | boolean | true when nothing about this message will change any more. Stop polling. |
reply | string | null | What the agent said, once it has answered. |
jobs required | array of Job | The pieces of work this message started, each with its own status. |
files required | array of File | Every file the message produced (handed over in the reply, or made by its jobs). |
notes required | array of string | Problems with attachments (a file that couldn't be fetched, files over the limit). |
credits_used required | number | Credits this message's jobs used so far. |
created_at required | string | |
updated_at required | string | When 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_by | string | The earliest deadline among queued jobs waiting for credits (24 hours after they were queued). |
retry_after_seconds | integer | With reason: "agent_busy": retry with the same Idempotency-Key after this many seconds. |
running | array of object | Present 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_id | string | exe_… |
message_id | string | |
work_id | string |
#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).
| Field | Type | Description |
|---|---|---|
job_id | string | exe_… for work that ran or is queued; ref_… for work refused before it was queued. |
kind | string | One of execution, refusal. |
role | string | follow_up when the message joined work that was already running; its status follows that work. One of execution, follow_up. |
work_id | string | null | The chat the work runs in. |
title | string | null | |
status | string JobStatus | One of running, completed, failed, cancelled, blocked, expired. |
reason | string | null | For blocked, failed, cancelled and expired: out_of_credits, hosting_fees, too_many_running, execution_failed, stopped, queue_expired, watch_expired, result_unknown. |
final | boolean | false while running, and for queued work waiting for credits (it can still run). |
queued | boolean | null | true for work that is queued and waiting for credits; otherwise null. |
resume_by | string | null | For queued work waiting for credits, the deadline to add them (24 hours after it was queued). |
link | string | null | The work in the SuperCool app. |
started_at | string | null | |
settled_at | string | null | |
files | array of File |
#JobStatus
string One of running, completed, failed, cancelled, blocked, expired.
#File
A file the agent made or handed over.
| Field | Type | Description |
|---|---|---|
file_id | string | <work_id>/<path>. Use it with GET /files/{file_id} for a fresh link. |
name | string | |
content_type | string | null | |
kind | string | One of image, video, audio, doc, site. |
size | integer | null | Bytes. null for a site. |
url | string | null | A signed download link. It expires, and it is pinned to revision; a download answers 412 if the file changed since. |
revision | string | null | The file's version the link is pinned to. null for a site (its link always opens the live site). |
work_id | string | null | |
label | string | null |
#FileObject
A file the agent made or handed over.
| Field | Type | Description |
|---|---|---|
object | "file" | |
file_id | string | <work_id>/<path>. Use it with GET /files/{file_id} for a fresh link. |
name | string | |
content_type | string | null | |
kind | string | One of image, video, audio, doc, site. |
size | integer | null | Bytes. null for a site. |
url | string | null | A signed download link. It expires, and it is pinned to revision; a download answers 412 if the file changed since. |
revision | string | null | The file's version the link is pinned to. null for a site (its link always opens the live site). |
work_id | string | null | |
label | string | null |
#EventList
| Field | Type | Description |
|---|---|---|
object | "list" | |
data | array of Event | |
has_more | boolean | More entries are ready; call again with cursor right away. |
done | boolean | The message has nothing more to report. |
cursor | string | Pass this back as cursor for the next page. |
cursor_expired | boolean | Your 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.
| Field | Type | Description |
|---|---|---|
seq | integer | |
type | string | result (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_id | string | null | |
message_id | string | null | The message the entry belongs to, when known. |
at | string | |
job_id | string | The job (exe_…) the entry belongs to, when known. |
title | string | null | result |
text | string | null | result, agent_reply, state, turn_failed, turn_busy |
outcome | string | result One of completed, failed, stalled_credits. |
files | array of File | result, agent_reply |
state | string | state |
step | string | null | progress |
status | string | null | progress |
file_names | array of string | progress: names of files that just landed (download them from the result). |
delivered_sync | boolean | null | agent_reply: true when this reply was already in the POST /messages response. |
reason | string | null | turn_failed, turn_busy |
#Recovery
| Field | Type | Description |
|---|---|---|
id | string | |
object | "recovery" | |
work | array of object | |
work_id | string | |
state | string | One of settled, watching, recovering, exhausted. |
outcome | string | For settled, how it ended. |
#UploadCreate
| Field | Type | Description |
|---|---|---|
filename required | string | |
size required | integer | The exact size in bytes (up to 500 MB). The upload is capped at this size. |
content_type | string | null | The MIME type. Guessed from the file name when omitted. The POST must send the same Content-Type field (it is in fields). |
#UploadCreated
| Field | Type | Description |
|---|---|---|
object | "upload" | |
upload_id | string | |
url | string | Where to POST the file (multipart/form-data). |
fields | object | Form fields to send with the file, exactly as given. |
expires_at | string | The presigned POST works until then (15 minutes). |
max_bytes | integer |
#Upload
| Field | Type | Description |
|---|---|---|
object | "upload" | |
upload_id | string | |
status | string | One of ready. |
filename | string | |
content_type | string | |
size | integer |
#WorkList
| Field | Type | Description |
|---|---|---|
object | "list" | |
data | array of WorkSummary |
#WorkSummary
| Field | Type | Description |
|---|---|---|
object | "work" | |
work_id | string | |
title | string | null | |
preview | string | null | |
link | string |
#Work
| Field | Type | Description |
|---|---|---|
object | "work" | |
work_id | string | |
title | string | null | |
status | string | running, stalled_credits (stopped until credits are added), or idle. One of running, stalled_credits, idle. |
text | string | The latest result text, from from_char. |
next_from_char | integer | null | Pass as from_char to read on; null when there's no more. |
link | string | |
files | array of File | The most recent files (up to 8). |
#Account
| Field | Type | Description |
|---|---|---|
object | "account" | |
user_id | string | |
email | string | null | |
name | string | null | |
plan | string | null | |
credits | number | null | Credits available now. |
agent | object | |
name | string | |
key | object | |
id | string | |
name | string | |
limits | object | |
running_jobs | integer | Running jobs allowed per account (5). |
requests_per_minute | integer | Requests 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
| Field | Type | Description |
|---|---|---|
url required | string | A public https URL (port 443 or 8443, up to 2,000 characters). |
events | array of string | null | Events to receive, or ["*"] for all (the default). |
description | string | null | Up to 120 characters. |
#WebhookUpdate
| Field | Type | Description |
|---|---|---|
url | string | null | |
events | array of string | null | |
description | string | null | |
enabled | boolean | null | true turns the endpoint back on and clears its failure state; false turns it off. |
#WebhookEndpoint
| Field | Type | Description |
|---|---|---|
object | "webhook_endpoint" | |
id | string | whe_… |
url | string | |
events | array of string | |
description | string | null | |
created_at | string | |
disabled_at | string | null | Set when the endpoint is off (by you, or after 3 days of failed deliveries). |
disabled_reason | string | null | |
failing_since | string | null | When deliveries started failing. Cleared by the next success. |
#WebhookEndpointWithSecret
| Field | Type | Description |
|---|---|---|
object | "webhook_endpoint" | |
id | string | whe_… |
url | string | |
events | array of string | |
description | string | null | |
created_at | string | |
disabled_at | string | null | Set when the endpoint is off (by you, or after 3 days of failed deliveries). |
disabled_reason | string | null | |
failing_since | string | null | When deliveries started failing. Cleared by the next success. |
secret | string | The signing secret (whsec_…). Shown only in this response. |
#WebhookEndpointDetail
| Field | Type | Description |
|---|---|---|
object | "webhook_endpoint" | |
id | string | whe_… |
url | string | |
events | array of string | |
description | string | null | |
created_at | string | |
disabled_at | string | null | Set when the endpoint is off (by you, or after 3 days of failed deliveries). |
disabled_reason | string | null | |
failing_since | string | null | When deliveries started failing. Cleared by the next success. |
recent_deliveries | array of WebhookDelivery |
#WebhookEndpointList
| Field | Type | Description |
|---|---|---|
object | "list" | |
data | array of WebhookEndpoint |
#WebhookDelivery
| Field | Type | Description |
|---|---|---|
object | "webhook_delivery" | |
id | string | The delivery id (evt_…), sent as webhook-id. |
event | string WebhookEventType | 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. |
status | string | One of pending, delivered, cancelled, dead. |
attempts | integer | |
last_status_code | integer | null | |
last_error | string | null | |
last_response | string | null | The first 4 KB of your endpoint's last response. |
last_ms | integer | null | |
created_at | string | |
delivered_at | string | null | |
next_attempt_at | string | null | |
message_id | string | null |
#RotatedSecret
| Field | Type | Description |
|---|---|---|
object | "webhook_endpoint" | |
id | string | |
secret | string |
#Deleted
| Field | Type | Description |
|---|---|---|
object | "webhook_endpoint" | |
id | string | |
deleted | true |
#JobEvent
| Field | Type | Description |
|---|---|---|
type | string | One of job.completed, job.failed, job.blocked, job.resumed, job.expired. |
timestamp | string | |
data | object | |
object | "job" | |
message_id | string | |
job_id | string | exe_… for work that ran or is queued; ref_… for work refused before it was queued. |
kind | string | One of execution, refusal. |
role | string | follow_up when the message joined work that was already running; its status follows that work. One of execution, follow_up. |
work_id | string | null | The chat the work runs in. |
title | string | null | |
status | string JobStatus | One of running, completed, failed, cancelled, blocked, expired. |
reason | string | null | For blocked, failed, cancelled and expired: out_of_credits, hosting_fees, too_many_running, execution_failed, stopped, queue_expired, watch_expired, result_unknown. |
final | boolean | false while running, and for queued work waiting for credits (it can still run). |
queued | boolean | null | true for work that is queued and waiting for credits; otherwise null. |
resume_by | string | null | For queued work waiting for credits, the deadline to add them (24 hours after it was queued). |
link | string | null | The work in the SuperCool app. |
started_at | string | null | |
settled_at | string | null | |
files | array of File |
#MessageEvent
| Field | Type | Description |
|---|---|---|
type | string | One of message.completed, message.partial, message.failed, message.needs_input, message.blocked, message.expired. |
timestamp | string | |
data | Message | A message you sent and everything that came of it. |
#Error
Every API error has this shape.
| Field | Type | Description |
|---|---|---|
error required | string ErrorCode | 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. |
message required | string | What went wrong, for people. |
request_id required | string | The request's id (req_…), also in the Request-Id header. |
docs required | string | A link to this error code's documentation. |
problems | array of object | With invalid_request: what doesn't match, one entry per problem (up to 10). |
field | string | null | The dotted location of the field (like files.0.url or wait), or null for the body as a whole. |
problem | string | |
cursor | string | With 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.