{"openapi":"3.1.0","info":{"title":"SingleStaff API","version":"1.0.0","description":"Place AI phone calls and get structured results. Authenticate with `Authorization: Bearer <API key>`. Test keys (ss_test_…) simulate calls with an AI-played callee: nothing is dialed or charged."},"servers":[{"url":"https://www.singlestaff.com/api/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from LLM keys & API: ss_live_… (live) or ss_test_… (test)."}},"schemas":{"CreateCall":{"type":"object","properties":{"botId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A saved bot. Either botId or agent.systemPrompt is required."},"agent":{"type":"object","properties":{"systemPrompt":{"type":"string","minLength":1,"maxLength":20000},"firstMessage":{"type":"string","maxLength":1000},"disclosure":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"language":{"type":"string","minLength":2,"maxLength":10},"voice":{"type":"string","minLength":1,"maxLength":80},"temperature":{"type":"number","minimum":0,"maximum":1.5},"maxCallSeconds":{"type":"integer","minimum":30,"maximum":3600},"collect":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"type":{"default":"text","type":"string","enum":["text","number","boolean","date"]},"description":{"default":"","type":"string","maxLength":300},"input":{"default":"voice","type":"string","enum":["voice","keypad","either"]},"options":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","enum":["0","1","2","3","4","5","6","7","8","9","*"]},"label":{"type":"string","minLength":1,"maxLength":60}},"required":["key","label"]}},"min":{"type":"integer","minimum":0,"maximum":999999999},"max":{"type":"integer","minimum":0,"maximum":999999999}},"required":["name"]}},"outcomes":{"maxItems":20,"type":"array","items":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"}},"optOutKey":{"anyOf":[{"type":"string","enum":["0","1","2","3","4","5","6","7","8","9","*"]},{"type":"null"}]}},"description":"An inline agent, or overrides on the bot. Fields left out come from the bot (or platform defaults)."},"to":{"type":"string","example":"+919876543210","description":"The number to call, in international format (E.164)."},"trunkId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A SIP trunk to call through (defaults from your account)."},"callerIdId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A verified caller ID to call from (defaults from your account)."},"timezone":{"type":"string","description":"The callee's IANA time zone, for calling hours (defaults from the number)."},"variables":{"default":{},"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":2000},"description":"Values for {{placeholders}} in the prompt and first message, e.g. {\"name\": \"Asha\"}."},"collect":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"type":{"default":"text","type":"string","enum":["text","number","boolean","date"]},"description":{"default":"","type":"string","maxLength":300},"input":{"default":"voice","type":"string","enum":["voice","keypad","either"]},"options":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","enum":["0","1","2","3","4","5","6","7","8","9","*"]},"label":{"type":"string","minLength":1,"maxLength":60}},"required":["key","label"]}},"min":{"type":"integer","minimum":0,"maximum":999999999},"max":{"type":"integer","minimum":0,"maximum":999999999}},"required":["name"]},"description":"Information to find out during the call (shortcut for agent.collect)."},"outcomes":{"maxItems":20,"type":"array","items":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"description":"Possible call outcomes, snake_case (shortcut for agent.outcomes)."},"metadata":{"default":{},"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"anyOf":[{"type":"string","maxLength":1000},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Your own data (up to 50 keys), returned with the call and in webhooks."},"webhookUrl":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"Receives this call's events instead of the bot's webhook URL."},"scheduledAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Dial no earlier than this time. Outside calling hours, the call waits until they open."},"retry":{"type":"object","properties":{"attempts":{"type":"integer","minimum":1,"maximum":5},"delayMinutes":{"default":30,"type":"integer","minimum":5,"maximum":1440}},"required":["attempts"],"description":"Call again when unanswered or busy: attempts (tries in all) and delayMinutes between them. Default: one try."},"simulate":{"type":"object","properties":{"persona":{"type":"string","minLength":1,"maxLength":2000},"answer":{"default":"answered","type":"string","enum":["answered","no_answer","busy","voicemail"]}},"description":"Test keys only: how the AI-played callee behaves (persona) and whether they answer."}},"required":["to"]},"CollectField":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"type":{"default":"text","type":"string","enum":["text","number","boolean","date"]},"description":{"default":"","type":"string","maxLength":300},"input":{"default":"voice","type":"string","enum":["voice","keypad","either"]},"options":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","enum":["0","1","2","3","4","5","6","7","8","9","*"]},"label":{"type":"string","minLength":1,"maxLength":60}},"required":["key","label"]}},"min":{"type":"integer","minimum":0,"maximum":999999999},"max":{"type":"integer","minimum":0,"maximum":999999999}},"required":["name"]},"Call":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","dialing","ringing","in_progress","completed","no_answer","busy","failed","voicemail","blocked","cancelled"]},"test":{"type":"boolean","description":"Made with a test key: simulated, never dialed or charged."},"botId":{"type":["string","null"],"format":"uuid"},"agent":{"type":["object","null"]},"toNumber":{"type":"string"},"fromNumber":{"type":"string"},"variables":{"type":"object","additionalProperties":{"type":"string"}},"metadata":{"type":"object"},"webhookUrl":{"type":["string","null"]},"idempotencyKey":{"type":["string","null"]},"endReason":{"type":["string","null"],"enum":["bot_ended","hangup","transferred","max_duration","voicemail","opted_out","error",null]},"blockedReason":{"type":["string","null"]},"hangupCause":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"costCents":{"type":["integer","null"]},"result":{"type":["object","null"],"description":"Filled in once the call has ended and been analysed; null before.","properties":{"outcome":{"type":["string","null"],"description":"One of the call's outcomes, or null if none clearly applied."},"collected":{"type":"object","additionalProperties":{"type":["string","number","boolean","null"]},"description":"Every requested field; null when the callee didn't provide it."},"summary":{"type":"string"}}},"scheduledAt":{"type":"string","format":"date-time"},"answeredAt":{"type":["string","null"],"format":"date-time"},"endedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"CallResult":{"type":["object","null"],"description":"Filled in once the call has ended and been analysed; null before.","properties":{"outcome":{"type":["string","null"],"description":"One of the call's outcomes, or null if none clearly applied."},"collected":{"type":"object","additionalProperties":{"type":["string","number","boolean","null"]},"description":"Every requested field; null when the callee didn't provide it."},"summary":{"type":"string"}}},"TranscriptTurn":{"type":"object","properties":{"role":{"type":"string","enum":["assistant","user","system"]},"text":{"type":"string","description":"Keypad presses appear as \"[Pressed 4]\"."},"at":{"type":"string","format":"date-time"}}},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"callId":{"type":["string","null"],"format":"uuid"},"event":{"type":"string"},"url":{"type":"string"},"test":{"type":"boolean"},"status":{"type":"string","enum":["pending","succeeded","failed"]},"attempts":{"type":"integer"},"lastStatusCode":{"type":["integer","null"]},"lastError":{"type":["string","null"]},"nextAttemptAt":{"type":"string","format":"date-time"},"deliveredAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"paths":{"/calls":{"post":{"operationId":"createCall","summary":"Create a call","description":"Queues an AI call. Send an Idempotency-Key header so retries never dial twice.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Retrying with the same key and body returns the original call (200)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCall"}}}},"responses":{"200":{"description":"Idempotent replay: the call created by an earlier request with this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency-Key reused with a different body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate or test-call limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listCalls","summary":"List calls","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["queued","dialing","ringing","in_progress","completed","no_answer","busy","failed","voicemail","blocked","cancelled"]}},{"name":"campaignId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Call"}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/calls/{id}":{"get":{"operationId":"getCall","summary":"Get a call with its result and transcript","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The call","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Call"},{"type":"object","properties":{"transcript":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptTurn"}}}}]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/calls/{id}/cancel":{"post":{"operationId":"cancelCall","summary":"Cancel a queued call","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/calls/{id}/recording":{"get":{"operationId":"getRecording","summary":"Download the call recording","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"WAV audio","content":{"audio/wav":{}}},"404":{"description":"No recording"}}}},"/webhooks/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"List webhook deliveries","parameters":[{"name":"callId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","succeeded","failed"]}},{"name":"event","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/deliveries/{id}/resend":{"post":{"operationId":"resendWebhook","summary":"Deliver a webhook again","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Queued"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/test":{"post":{"operationId":"sendTestWebhook","summary":"Send a signed ping event to a URL","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"responses":{"202":{"description":"Queued"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"call.answered":{"post":{"summary":"call.answered","description":"The callee picked up. Signed with X-SingleStaff-Signature: t=<unix>,v1=<HMAC-SHA256 of \"<t>.<raw body>\">.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique per event: use it to ignore duplicates."},"event":{"type":"string","const":"call.answered"},"created":{"type":"string","format":"date-time"},"test":{"type":"boolean"},"data":{"type":"object","properties":{"call":{"$ref":"#/components/schemas/Call"}}}}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event; anything else is retried for about a day."}}}},"call.completed":{"post":{"summary":"call.completed","description":"An answered call ended and was analysed; call.result holds the outcome, collected fields and summary. Signed with X-SingleStaff-Signature: t=<unix>,v1=<HMAC-SHA256 of \"<t>.<raw body>\">.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique per event: use it to ignore duplicates."},"event":{"type":"string","const":"call.completed"},"created":{"type":"string","format":"date-time"},"test":{"type":"boolean"},"data":{"type":"object","properties":{"call":{"$ref":"#/components/schemas/Call"},"transcript":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptTurn"}}}}}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event; anything else is retried for about a day."}}}},"call.failed":{"post":{"summary":"call.failed","description":"The call never connected: call.status is no_answer, busy, failed or blocked. Signed with X-SingleStaff-Signature: t=<unix>,v1=<HMAC-SHA256 of \"<t>.<raw body>\">.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique per event: use it to ignore duplicates."},"event":{"type":"string","const":"call.failed"},"created":{"type":"string","format":"date-time"},"test":{"type":"boolean"},"data":{"type":"object","properties":{"call":{"$ref":"#/components/schemas/Call"}}}}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event; anything else is retried for about a day."}}}},"call.data":{"post":{"summary":"call.data","description":"The bot sent data during the call. Signed with X-SingleStaff-Signature: t=<unix>,v1=<HMAC-SHA256 of \"<t>.<raw body>\">.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique per event: use it to ignore duplicates."},"event":{"type":"string","const":"call.data"},"created":{"type":"string","format":"date-time"},"test":{"type":"boolean"},"data":{"type":"object","properties":{"call":{"$ref":"#/components/schemas/Call"},"name":{"type":"string"},"data":{"type":"object"}}}}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event; anything else is retried for about a day."}}}}}}