Authentication
Every request is authenticated with a workspace-scoped API key. Keys are created and revoked from inside the workspace, never via the API. A single key authorizes exactly one workspace; if you manage several workspaces, create one key per workspace.
Create an API key
- Sign in to RadiusOS and open the workspace you want to expose.
- Go to
/{org}/{workspace}/settings/api-keys. - Click "Create API key", name it (for example "Zapier - Acme"), and copy the key. The key is shown once.
- Paste it into Zapier (or your custom client). Every key looks like
ros_<64 hex chars>.
Send the key with every request
Three ways, in priority order:
Authorization: Bearer ros_<key>- the canonical header used by the official Zapier integration.X-Api-Key: ros_<key>- convenient for tools whose UI only exposes a single header field.?api_key=ros_<key>- query-string fallback for quick curl tests. Avoid in production: query strings show up in server logs.
First request
curl https://www.radiusos.ai/api/zapier/auth/test \
-H "Authorization: Bearer ros_<your_api_key>"
# Response:
# {
# "org": { "id": "...", "name": "Acme Roofing", "slug": "acme-roofing" },
# "workspace": { "id": "...", "name": "Main", "slug": "main" },
# "label": "Acme Roofing / Main"
# }Rate limits
The API is currently fair-use. A small number of concrete limits live at the plan level rather than as raw per-second caps:
- Active Zaps per workspace. Free is capped at one active Zap; Pro and above are unlimited. A subscribe call that exceeds the cap returns
402 plan_limit_reached. - Contact creation cap. The create-contact action respects the workspace's plan-level contact limit (Free: 250). A create that exceeds the cap returns
402 contact_limit_reached. - Abuse protection. Bursty traffic that looks like a misbehaving client may be rate-limited at the edge with a
429. Retry after a few seconds.
If you have a legitimate use case that needs sustained high throughput, write to support@radiusos.ai and we'll raise the ceiling on your workspace.
Errors
Every error response is JSON with a stable shape. Use the error field for machine-readable branching and the message field for user-facing text. Additional fields may be added in the future, never removed.
{
"error": "<machine_readable_code>",
"message": "<human-readable explanation>"
}| Status | Error code | Means |
|---|---|---|
| 400 | invalid_json | missing_* | Request body or required field missing or malformed. Don't retry without fixing the request. |
| 401 | missing_api_key | invalid_api_key | Auth header missing or key unrecognized. Don't retry. |
| 402 | plan_limit_reached | contact_limit_reached | Plan limit hit. Retry will keep failing until the user upgrades or removes data. The body includes plan, limit, and upgradeUrl fields. |
| 404 | contact_not_found | Referenced resource does not exist in this workspace. |
| 429 | (none) | Edge rate limit. Retry with backoff. |
| 500 | internal_error | Unexpected server error. The full exception is logged on our side. Safe to retry. |
Authentication
/api/zapier/auth/testTest connection
Validates an API key and returns the workspace label. Zapier calls this once when a user connects their account so it can show a human-readable connection name. Useful for any client that wants to verify credentials before issuing real requests.
{
"org": { "id": "org_abc", "name": "Acme Roofing", "slug": "acme-roofing" },
"workspace": { "id": "ws_xyz", "name": "Main", "slug": "main" },
"label": "Acme Roofing / Main"
}| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | No Authorization header, X-Api-Key header, or ?api_key query parameter present. |
| 401 | invalid_api_key | The key is not recognized. Most commonly a typo, a key from a different workspace, or a deleted key. |
curl https://www.radiusos.ai/api/zapier/auth/test \
-H "Authorization: Bearer ros_<your_api_key>"Triggers (REST hooks)
/api/zapier/subscribeSubscribe to events
Registers a target URL to receive event payloads whenever the specified event fires in the workspace. Used by Zapier to wire a Zap to RadiusOS. The returned id must be passed to the unsubscribe endpoint when the Zap is disabled. Each subscription is signed with its own per-subscription HMAC secret for future signature verification.
| Field | Type | Description |
|---|---|---|
| event* | string | One of: contact.created, contact.updated, contact.stage_changed, task.created, task.completed. |
| target_url* | string | Absolute http(s) URL that will receive POSTed event payloads. |
{
"id": "wh_abc123",
"event": "contact.created",
"target_url": "https://hooks.zapier.com/hooks/standard/..."
}| Status | Code | When |
|---|---|---|
| 400 | invalid_json | Body is not valid JSON. |
| 400 | invalid_event | event is not one of the supported names. |
| 400 | invalid_target_url | target_url is missing or not an http(s) URL. |
| 401 | missing_api_key | invalid_api_key | See Authentication. |
| 402 | plan_limit_reached | Workspace is at its active-Zap cap for the current plan. |
curl -X POST https://www.radiusos.ai/api/zapier/subscribe \
-H "Authorization: Bearer ros_<your_api_key>" \
-H "Content-Type: application/json" \
-d '{
"event": "contact.created",
"target_url": "https://example.com/inbound"
}'/api/zapier/subscribe/{id}Unsubscribe from events
Deletes a subscription previously created by the subscribe endpoint. The id is the value returned by subscribe. Idempotent: returns 204 even if the subscription is already gone, so retries are safe. Tenant-scoped: a key from a different workspace cannot delete another workspace's subscription.
(empty body, status 204)| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | invalid_api_key | See Authentication. |
curl -X DELETE https://www.radiusos.ai/api/zapier/subscribe/wh_abc123 \
-H "Authorization: Bearer ros_<your_api_key>"/api/zapier/triggers/contactsRecent contacts (polling fallback + sample data)
Returns the most recent contacts in the workspace, newest first. Zapier uses this as the sample-data source when a user is setting up a Zap and as a polling fallback when REST hooks aren't available. Custom clients can poll this to back-fill events they may have missed.
| Field | Type | Description |
|---|---|---|
| limit | integer | Optional. Number of contacts to return. Default 5. Min 1, max 25. |
[
{
"id": "ctc_abc",
"name": "Sarah Chen",
"email": "sarah@acme.com",
"phone": "+15555550100",
"stage": "discovery",
"created_at": "2026-05-08T14:22:00.000Z",
...
},
...
]| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | invalid_api_key | See Authentication. |
curl 'https://www.radiusos.ai/api/zapier/triggers/contacts?limit=5' \
-H "Authorization: Bearer ros_<your_api_key>"/api/zapier/triggers/tasksRecent tasks (polling fallback + sample data)
Returns the most recent tasks in the workspace. Pass ?completed=true to filter to completed tasks ordered by completion time - used by the task-completed Zapier trigger so its sample matches reality.
| Field | Type | Description |
|---|---|---|
| limit | integer | Optional. Number of tasks to return. Default 5. Min 1, max 25. |
| completed | boolean | Optional. When 'true', returns only completed tasks ordered by completedAt desc. |
[
{
"id": "tsk_abc",
"title": "Send proposal",
"contact_id": "ctc_abc",
"due_date": "2026-05-10T00:00:00.000Z",
"completed_at": null,
"priority": "normal",
...
},
...
]| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | invalid_api_key | See Authentication. |
curl 'https://www.radiusos.ai/api/zapier/triggers/tasks?completed=true&limit=5' \
-H "Authorization: Bearer ros_<your_api_key>"Actions
/api/zapier/actions/create-contactCreate a contact
Creates a contact in the connected workspace. The only required field is name; every other field is optional, matching the in-app contact form. The new contact is placed in the workspace template's first stage unless an explicit stage is supplied.
| Field | Type | Description |
|---|---|---|
| name* | string | Display name. Trimmed; empty strings are rejected. |
| string | Optional. Validated against a permissive RFC pattern. | |
| phone | string | Optional. Stored verbatim; not normalized. |
| stage | string | Optional. Must match a stage key from the workspace template (see /api/zapier/dropdowns/stages). Falls back to the first stage if omitted or invalid. |
| address_line1 | string | Optional. |
| city | string | Optional. |
| state | string | Optional. |
| postal_code | string | Optional. |
| country | string | Optional. |
| notes | string | Optional. Plain text. |
{
"id": "ctc_abc",
"name": "Sarah Chen",
"email": "sarah@acme.com",
"stage": "discovery",
"created_at": "2026-05-12T17:00:00.000Z",
...
}| Status | Code | When |
|---|---|---|
| 400 | invalid_json | Body is not valid JSON. |
| 400 | missing_name | name is empty after trimming. |
| 400 | invalid_email | email is present but does not match a valid email shape. |
| 401 | missing_api_key | invalid_api_key | See Authentication. |
| 402 | contact_limit_reached | Workspace is at its plan's contact cap. Free is capped at 250 contacts. |
| 500 | internal_error | Unexpected server error. The full exception has been logged. Safe to retry. |
curl -X POST https://www.radiusos.ai/api/zapier/actions/create-contact \
-H "Authorization: Bearer ros_<your_api_key>" \
-H "Content-Type: application/json" \
-d '{
"name": "Sarah Chen",
"email": "sarah@acme.com",
"stage": "discovery"
}'/api/zapier/actions/create-taskCreate a task
Creates a task in the workspace. Optionally linked to a contact via contact_id (the value Zapier's Contact dropdown supplies). due_date accepts any ISO 8601 timestamp the JavaScript Date constructor can parse, so upstream triggers like Calendar events plug in directly.
| Field | Type | Description |
|---|---|---|
| title* | string | Task title. Empty strings are rejected. |
| contact_id | string | Optional. Must reference a contact in the same workspace, or the request 404s. Leave empty for an unattached task. |
| due_date | string | Optional. Any string the Date constructor can parse, typically ISO 8601. |
| priority | string | Optional. One of: low | normal | high | urgent. Defaults to normal. |
| assigned_to | string | Optional. Clerk user id of the workspace member to assign. |
| notes | string | Optional. Plain text. |
{
"id": "tsk_abc",
"title": "Send proposal",
"contact_id": "ctc_abc",
"due_date": "2026-05-15T00:00:00.000Z",
"priority": "normal",
"created_at": "2026-05-12T17:00:00.000Z",
...
}| Status | Code | When |
|---|---|---|
| 400 | invalid_json | Body is not valid JSON. |
| 400 | missing_title | title is empty after trimming. |
| 401 | missing_api_key | invalid_api_key | See Authentication. |
| 404 | contact_not_found | contact_id was supplied but does not belong to this workspace. |
| 500 | internal_error | Unexpected server error. The full exception has been logged. Safe to retry. |
curl -X POST https://www.radiusos.ai/api/zapier/actions/create-task \
-H "Authorization: Bearer ros_<your_api_key>" \
-H "Content-Type: application/json" \
-d '{
"title": "Send proposal",
"contact_id": "ctc_abc",
"due_date": "2026-05-15T00:00:00.000Z",
"priority": "high"
}'/api/zapier/actions/move-stageMove contact to a new stage
Moves a contact to a different pipeline stage. The stage must match a key in the workspace's template (use /api/zapier/dropdowns/stages to enumerate valid keys). If the contact is already in the target stage the request succeeds and returns the current contact without writing an activity row.
| Field | Type | Description |
|---|---|---|
| contact_id* | string | ID of the contact to move. |
| stage* | string | Stage key from the workspace template. |
{
"id": "ctc_abc",
"name": "Sarah Chen",
"stage": "won",
...
}| Status | Code | When |
|---|---|---|
| 400 | invalid_json | Body is not valid JSON. |
| 400 | missing_fields | contact_id or stage missing. |
| 400 | invalid_stage | stage does not exist on this workspace's template. |
| 401 | missing_api_key | invalid_api_key | See Authentication. |
| 404 | contact_not_found | contact_id does not belong to this workspace. |
| 500 | internal_error | Unexpected server error. The full exception has been logged. Safe to retry. |
curl -X POST https://www.radiusos.ai/api/zapier/actions/move-stage \
-H "Authorization: Bearer ros_<your_api_key>" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "ctc_abc",
"stage": "won"
}'Dropdowns (helpers)
/api/zapier/dropdowns/contactsContact picker source
Returns a typeahead-friendly list of contacts as {id, label} pairs. Used by Zapier to populate the Contact picker when a user is setting up a Create Task or Move Stage action. Custom clients can use the same shape to build their own pickers.
| Field | Type | Description |
|---|---|---|
| q | string | Optional. Filters contacts whose name, email, or phone contains the value (case-insensitive). |
[
{ "id": "ctc_abc", "label": "Sarah Chen (sarah@acme.com)" },
{ "id": "ctc_def", "label": "John Park" },
...
]| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | invalid_api_key | See Authentication. |
curl 'https://www.radiusos.ai/api/zapier/dropdowns/contacts?q=sarah' \
-H "Authorization: Bearer ros_<your_api_key>"/api/zapier/dropdowns/stagesStage picker source
Returns the workspace's pipeline stages as {id, label} pairs in template order. The id field is the stage key you pass back to the move-stage and create-contact endpoints.
[
{ "id": "discovery", "label": "Discovery" },
{ "id": "proposal", "label": "Proposal" },
{ "id": "negotiation","label": "Negotiation" },
{ "id": "won", "label": "Closed Won" },
...
]| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | invalid_api_key | See Authentication. |
curl https://www.radiusos.ai/api/zapier/dropdowns/stages \
-H "Authorization: Bearer ros_<your_api_key>"Versioning
The API is unversioned in the URL today: there is no /v1/ prefix. Backwards-compatible changes (new optional fields, new endpoints, additional error codes) ship continuously without notice. Breaking changes (renaming a field, removing an endpoint, changing required-vs-optional) will be announced at least 60 days in advance to every workspace with active API keys, and a versioned path will be introduced if the change is large enough to need one.
Support
Bugs, edge cases, feature requests, or you just can't make a call work? Email support@radiusos.ai with the request URL, method, body, and response. We typically reply within one business day.
For the official Zapier integration specifically, the easiest path is the Zapier help article.