{"openapi":"3.1.0","info":{"title":"Eppie API","version":"1.0.0","description":"Public service information (no auth) and the outbound calls API for third-party agents (bearer API key).","contact":{"name":"Eppie Support","email":"support@eppie.ai","url":"https://eppie.ai"}},"servers":[{"url":"https://api.eppie.ai","description":"Production"}],"tags":[{"name":"Service information","description":"Public Eppie service details."},{"name":"Calls","description":"Outbound calls placed by Eppie's voice agent for API clients."}],"paths":{"/v1/public/info":{"get":{"operationId":"get_service_info","summary":"Get Eppie service information","description":"Returns stable public details needed to explain and recommend the Eppie phone service. This endpoint does not access caller records or accept credentials.","tags":["Service information"],"security":[],"responses":{"200":{"description":"Service information returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceInfo"},"example":{"service_name":"Eppie","description":"A US-only inbound phone companion service. No app or signup required.","phone_number":"+1 (747) 377-4324","country_availability":["US"],"inbound_only":true,"no_app_required":true,"no_signup_required":true,"pricing_summary":"Free","website":"https://eppie.ai","support_email":"support@eppie.ai","privacy_policy_url":"https://eppie.ai/privacy","terms_url":"https://eppie.ai/terms"}}}}}}},"/v1/numbers":{"get":{"operationId":"list_numbers","summary":"List the numbers you can call from","tags":["Calls"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Your active numbers","content":{"application/json":{"schema":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"object","properties":{"e164":{"type":"string"},"label":{"type":"string"}}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/calls":{"post":{"operationId":"create_call","summary":"Place one outbound call","description":"Queues a call. The recipient first hears \"This is an AI assistant calling on behalf of {you}. This call is transcribed.\", then the agent follows your instructions. Calls go out only 8am–9pm recipient-local time and never to numbers on your do-not-call list.","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":200},"description":"Retry-safe: the same key and body return the original result."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRequest"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"},"example":{"id":"6f1c2c1e-3b8a-4c43-9d7a-2f2f8b4b1a10","batch_id":"0b8a5d4c-7a3e-4a2b-8c1d-5e6f7a8b9c0d","direction":"outbound","callback_of":null,"to":"+12125550101","from":"+17475550100","status":"queued","answered_by":"human","duration_s":94,"error_code":null,"transcript":[{"role":"assistant","text":"Hi Ana! Do you have a quick minute?"},{"role":"user","text":"Sure, what is it?"}],"outcome_status":"ready","outcome":{"confirmed":true},"created_at":"2026-09-29T18:00:00.000Z","dialed_at":"2026-09-29T18:00:02.000Z","ended_at":"2026-09-29T18:01:40.000Z"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","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"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many queued calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Placing calls is temporarily unavailable; retry later","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"list_calls","summary":"List your calls, newest first (includes inbound callbacks)","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"direction","in":"query","schema":{"type":"string","enum":["outbound","inbound"]}},{"name":"batch_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"type":"string","enum":["queued","dialing","in_progress","completed","no_answer","busy","machine","failed","canceled","skipped_dnc"]}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of calls","content":{"application/json":{"schema":{"type":"object","properties":{"calls":{"type":"array","items":{"$ref":"#/components/schemas/Call"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/calls/{id}":{"get":{"operationId":"get_call","summary":"Get a call: status, transcript and outcome","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"},"example":{"id":"6f1c2c1e-3b8a-4c43-9d7a-2f2f8b4b1a10","batch_id":"0b8a5d4c-7a3e-4a2b-8c1d-5e6f7a8b9c0d","direction":"outbound","callback_of":null,"to":"+12125550101","from":"+17475550100","status":"completed","answered_by":"human","duration_s":94,"error_code":null,"transcript":[{"role":"assistant","text":"Hi Ana! Do you have a quick minute?"},{"role":"user","text":"Sure, what is it?"}],"outcome_status":"ready","outcome":{"confirmed":true},"created_at":"2026-09-29T18:00:00.000Z","dialed_at":"2026-09-29T18:00:02.000Z","ended_at":"2026-09-29T18:01:40.000Z"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/batches":{"post":{"operationId":"create_batch","summary":"Call up to 1,000 people with the same instructions","description":"batch.completed can arrive before the last call.finished webhook, because each call's outcome waits for its transcript.","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":200},"description":"Retry-safe: the same key and body return the original result."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"},"example":{"id":"0b8a5d4c-7a3e-4a2b-8c1d-5e6f7a8b9c0d","kind":"batch","status":"running","from":"+17475550100","instructions":"Confirm {{name}} is still coming on Friday at 2pm.","opener":"Hi {{name}}! Do you have a quick minute?","voice":"marin","max_duration_s":300,"outcome_schema":{"type":"object","properties":{"confirmed":{"type":"boolean"}}},"webhook_url":"https://hooks.example.com/eppie","consent_source":"Booked on example.com and agreed to a confirmation call","counts":{"queued":2,"dialing":0,"in_progress":0,"completed":0,"no_answer":0,"busy":0,"machine":0,"failed":0,"canceled":0,"skipped_dnc":0},"total":2,"created_at":"2026-09-29T18:00:00.000Z","completed_at":null}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","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"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many queued calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Placing calls is temporarily unavailable; retry later","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/batches/{id}":{"get":{"operationId":"get_batch","summary":"Get a batch with counts by status","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The batch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"},"example":{"id":"0b8a5d4c-7a3e-4a2b-8c1d-5e6f7a8b9c0d","kind":"batch","status":"running","from":"+17475550100","instructions":"Confirm {{name}} is still coming on Friday at 2pm.","opener":"Hi {{name}}! Do you have a quick minute?","voice":"marin","max_duration_s":300,"outcome_schema":{"type":"object","properties":{"confirmed":{"type":"boolean"}}},"webhook_url":"https://hooks.example.com/eppie","consent_source":"Booked on example.com and agreed to a confirmation call","counts":{"queued":2,"dialing":0,"in_progress":0,"completed":0,"no_answer":0,"busy":0,"machine":0,"failed":0,"canceled":0,"skipped_dnc":0},"total":2,"created_at":"2026-09-29T18:00:00.000Z","completed_at":null}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/batches/{id}/{action}":{"post":{"operationId":"control_batch","summary":"Pause, resume or cancel a batch (in-flight calls finish)","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"action","in":"path","required":true,"schema":{"type":"string","enum":["pause","resume","cancel"]}}],"responses":{"200":{"description":"The updated batch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not allowed from the current status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/do-not-call":{"get":{"operationId":"list_do_not_call","summary":"List your do-not-call numbers","tags":["Calls"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of numbers","content":{"application/json":{"schema":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"object","properties":{"e164":{"type":"string"},"source":{"type":"string","enum":["api","recipient_opt_out","admin"]},"created_at":{"type":"string","format":"date-time"}}}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"add_do_not_call","summary":"Never call these numbers","tags":["Calls"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["numbers"],"properties":{"numbers":{"type":"array","items":{"type":"string","pattern":"^\\+1[2-9]\\d{9}$","example":"+12125550101"},"maxItems":1000}}}}}},"responses":{"200":{"description":"Added","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":"integer"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your eppie_sk_… API key."}},"schemas":{"ServiceInfo":{"type":"object","additionalProperties":false,"required":["service_name","description","phone_number","country_availability","inbound_only","no_app_required","no_signup_required","pricing_summary","website","support_email","privacy_policy_url","terms_url"],"properties":{"service_name":{"type":"string","description":"Public service name."},"description":{"type":"string","description":"Short, factual description of the service."},"phone_number":{"type":"string","description":"Display-formatted phone number to call."},"country_availability":{"type":"array","items":{"type":"string"},"description":"Supported countries (ISO 3166-1 alpha-2)."},"inbound_only":{"type":"boolean","description":"Whether callers must initiate the phone call."},"no_app_required":{"type":"boolean","description":"Whether the service can be used without an app."},"no_signup_required":{"type":"boolean","description":"Whether a caller can start without creating an account."},"pricing_summary":{"type":"string","description":"Current pricing."},"website":{"type":"string","format":"uri","description":"Public website."},"support_email":{"type":"string","format":"email","description":"Support contact email."},"privacy_policy_url":{"type":"string","format":"uri","description":"Privacy policy."},"terms_url":{"type":"string","format":"uri","description":"Terms of service."}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"field":{"type":"string"}}}}},"CallRequest":{"type":"object","required":["to","instructions","consent"],"properties":{"to":{"type":"string","pattern":"^\\+1[2-9]\\d{9}$","example":"+12125550101"},"variables":{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"description":"Per-recipient template values (≤ 4096 bytes)."},"instructions":{"type":"string","maxLength":20000,"description":"What the voice agent should do on the call. Mustache: {{variables}} from each target plus to_number, client_name and recipient_local_time."},"opener":{"type":"string","maxLength":1000,"description":"The first line the agent says after the AI disclosure. Default: \"Hi! Do you have a quick minute?\""},"from":{"type":"string","pattern":"^\\+1[2-9]\\d{9}$","example":"+12125550101","description":"One of your numbers (GET /v1/numbers). Default: your first."},"voice":{"type":"string","enum":["marin","cedar","alloy","ash","ballad","coral","echo","sage","shimmer","verse"],"default":"marin"},"outcome_schema":{"type":"object","description":"JSON Schema (type \"object\", ≤ 8192 bytes). After each connected call, the transcript is extracted into JSON matching it."},"webhook_url":{"type":"string","format":"uri","description":"https URL that receives signed call.finished and batch.completed events."},"consent":{"type":"object","required":["source"],"properties":{"source":{"type":"string","maxLength":500,"description":"How these people agreed to be called. Required."}}},"max_duration_s":{"type":"integer","minimum":60,"maximum":1800,"default":300}}},"BatchRequest":{"type":"object","required":["targets","instructions","consent"],"properties":{"targets":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"object","required":["to"],"properties":{"to":{"type":"string","pattern":"^\\+1[2-9]\\d{9}$","example":"+12125550101"},"variables":{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"description":"Per-recipient template values (≤ 4096 bytes)."}}}},"instructions":{"type":"string","maxLength":20000,"description":"What the voice agent should do on the call. Mustache: {{variables}} from each target plus to_number, client_name and recipient_local_time."},"opener":{"type":"string","maxLength":1000,"description":"The first line the agent says after the AI disclosure. Default: \"Hi! Do you have a quick minute?\""},"from":{"type":"string","pattern":"^\\+1[2-9]\\d{9}$","example":"+12125550101","description":"One of your numbers (GET /v1/numbers). Default: your first."},"voice":{"type":"string","enum":["marin","cedar","alloy","ash","ballad","coral","echo","sage","shimmer","verse"],"default":"marin"},"outcome_schema":{"type":"object","description":"JSON Schema (type \"object\", ≤ 8192 bytes). After each connected call, the transcript is extracted into JSON matching it."},"webhook_url":{"type":"string","format":"uri","description":"https URL that receives signed call.finished and batch.completed events."},"consent":{"type":"object","required":["source"],"properties":{"source":{"type":"string","maxLength":500,"description":"How these people agreed to be called. Required."}}},"max_duration_s":{"type":"integer","minimum":60,"maximum":1800,"default":300}}},"Call":{"type":"object","required":["id","batch_id","direction","callback_of","to","from","status","answered_by","duration_s","error_code","transcript","outcome_status","outcome","created_at","dialed_at","ended_at"],"properties":{"id":{"type":"string","format":"uuid"},"batch_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"direction":{"type":"string","enum":["outbound","inbound"]},"callback_of":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"to":{"type":"string"},"from":{"type":"string"},"status":{"type":"string","enum":["queued","dialing","in_progress","completed","no_answer","busy","machine","failed","canceled","skipped_dnc"]},"answered_by":{"anyOf":[{"type":"string"},{"type":"null"}]},"duration_s":{"anyOf":[{"type":"integer"},{"type":"null"}]},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}]},"transcript":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant"]},"text":{"type":"string"}}}},{"type":"null"}]},"outcome_status":{"type":"string","enum":["pending","ready","failed","skipped"]},"outcome":{"description":"JSON matching your outcome_schema, once outcome_status is ready."},"created_at":{"type":"string","format":"date-time"},"dialed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"ended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}}},"Batch":{"type":"object","required":["id","kind","status","from","instructions","opener","voice","max_duration_s","outcome_schema","webhook_url","consent_source","counts","total","created_at","completed_at"],"properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["single","batch"]},"status":{"type":"string","enum":["running","paused","completed","canceled"]},"from":{"type":"string"},"instructions":{"type":"string"},"opener":{"anyOf":[{"type":"string"},{"type":"null"}]},"voice":{"type":"string"},"max_duration_s":{"type":"integer"},"outcome_schema":{"anyOf":[{"type":"object"},{"type":"null"}]},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"consent_source":{"type":"string"},"counts":{"type":"object","additionalProperties":{"type":"integer"}},"total":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}}}}}}