{"openapi":"3.0.3","info":{"title":"HelloTalk Developer API","version":"1.0.0","description":"Place voice calls from your own software.\n\nOne number per call, one recorded message, and — if you want it — what the customer pressed. The result reaches your server as an HMAC-signed webhook.\n\nFull explanation and examples: https://hellotalk.com.bd/docs/api","contact":{"url":"https://hellotalk.com.bd/contact"}},"servers":[{"url":"https://hellotalk.com.bd/api/v1","description":"The platform address — never changes"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Calls","description":"Place calls, read their status, cancel them"},{"name":"Audio","description":"For choosing which message to play"},{"name":"System","description":"Whether the key works"}],"paths":{"/calls":{"post":{"tags":["Calls"],"summary":"Place a call","description":"The call is queued and an id comes back immediately.\n\nOutside calling hours, or when every channel is busy, it waits as `queued` — it is not thrown away.\n\n**Send an `Idempotency-Key` header.** A second request under the same key places no new call; it replays the first response (200, `Idempotent-Replay: true`). A key is remembered for 24 hours.","operationId":"placeCall","security":[{"bearerAuth":["calls:write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A second request under the same key places no new call — the customer is not phoned twice.\n\nThe key should name the ATTEMPT, not only the order: the same key blocks a deliberate retry as well, so to call again within 24 hours use something like `order-8842-attempt-2`.\n\nNothing stops you calling the same number repeatedly — no number is ever blocked, only a repeated key is.","schema":{"type":"string","maxLength":100},"example":"order-8842-attempt-1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceCallRequest"},"examples":{"simple":{"summary":"Just play a message","value":{"to":"01712345678","audio_id":12,"reference":"invoice-8842","webhook_url":"https://shop.example.com/hashtech"}},"confirm":{"summary":"Confirm an order — press 1 for yes, 2 for no","value":{"to":"01712345678","audio_id":12,"reference":"order-8842","gather":{"digits":["1","2"],"repeat":2,"timeout":7},"webhook_url":"https://shop.example.com/hashtech","expires_in_minutes":120}},"agent":{"summary":"Press 1 to speak to an agent","value":{"to":"01712345678","audio_id":12,"gather":{"digits":["1","2"]},"connect":{"on_digit":"1","queue_id":4}}},"menu":{"summary":"A full IVR menu after the message","value":{"to":"01712345678","audio_id":12,"ivr_id":3}}}}}},"responses":{"202":{"description":"The call was accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"200":{"description":"Same Idempotency-Key — the earlier call is replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Calls"],"summary":"List of calls","operationId":"listCalls","security":[{"bearerAuth":["calls:read"]}],"parameters":[{"name":"reference","in":"query","required":false,"description":"Search by your own reference","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["queued","ringing","answered","busy","no_answer","failed","cancelled","expired"]}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"List of calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/calls/{id}":{"get":{"tags":["Calls"],"summary":"The status of one call","operationId":"getCall","security":[{"bearerAuth":["calls:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The numeric call id — the `1042` part of `call_1042` in the response.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The current status of the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/calls/{id}/cancel":{"post":{"tags":["Calls"],"summary":"Cancel a call","description":"Only while `queued`. Once the phone starts ringing it can no longer be cancelled.","operationId":"cancelCall","security":[{"bearerAuth":["calls:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The numeric call id — the `1042` part of `call_1042` in the response.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The call was cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/audios":{"get":{"tags":["Audio"],"summary":"Your audio that is ready to use","description":"Only audio that is ready appears. Conversion takes a few seconds after an upload; during that time it is absent from the list, because there is no file to play yet.","operationId":"listAudios","security":[{"bearerAuth":["audios:read"]}],"responses":{"200":{"description":"List of audio","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/ping":{"get":{"tags":["System"],"summary":"Whether the key works","description":"Needs no scope, so it can be used as a health check.","operationId":"ping","responses":{"200":{"description":"The key is valid","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"account":{"type":"string","example":"Rahim Store"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A key created on the Developer API page of your portal.\n\n`Authorization: Bearer kp_xxxxxxxxx_…` — the key is shown once and never again."}},"schemas":{"PlaceCallRequest":{"type":"object","required":["to","audio_id"],"properties":{"to":{"type":"string","description":"A Bangladeshi mobile number, in any familiar form — `01712345678`, `+8801712345678`, `8801712345678`।","example":"01712345678"},"audio_id":{"type":"integer","description":"Which message to play. Take it from `GET /audios` or from the portal.","example":12},"reference":{"type":"string","maxLength":100,"nullable":true,"description":"Your own id — it comes back in the webhook exactly as sent. It is the only key you have for matching the call in your own database.","example":"order-8842"},"webhook_url":{"type":"string","format":"uri","maxLength":500,"nullable":true,"description":"The result is POSTed here once the call ends. **https is required**, and the address must be publicly reachable — local or private networks are refused.","example":"https://shop.example.com/hashtech"},"expires_in_minutes":{"type":"integer","minimum":1,"maximum":10080,"default":1440,"description":"If the call cannot be placed within this, it becomes `expired` and a webhook is sent. Set it low when confirming a night order the next morning would be pointless."},"gather":{"$ref":"#/components/schemas/Gather"},"connect":{"$ref":"#/components/schemas/Connect"},"ivr_id":{"type":"integer","nullable":true,"description":"Your own IVR menu runs after the message. Cannot be combined with `gather` or `connect`."}}},"Gather":{"type":"object","nullable":true,"description":"Wait for a keypress after the message.","required":["digits"],"properties":{"digits":{"type":"array","minItems":1,"maxItems":12,"items":{"type":"string","enum":["0","1","2","3","4","5","6","7","8","9","*","#"]},"example":["1","2"]},"repeat":{"type":"integer","minimum":1,"maximum":5,"default":2,"description":"How many more times the message plays when nobody presses"},"timeout":{"type":"integer","minimum":3,"maximum":30,"default":7,"description":"Seconds to wait each time"}}},"Connect":{"type":"object","nullable":true,"description":"Put the customer straight through to an agent when they press a given key.\n\nCannot be used without `gather`, and `on_digit` must be one of `gather.digits`.\n\nPrefer `queue_id`: an extension that is busy or offline drops the call, while a queue has hold music and a failover.","required":["on_digit"],"properties":{"on_digit":{"type":"string","example":"1"},"queue_id":{"type":"integer","nullable":true,"description":"Give exactly one destination"},"extension_id":{"type":"integer","nullable":true}}},"Call":{"type":"object","properties":{"id":{"type":"string","example":"call_1042"},"status":{"type":"string","enum":["queued","ringing","answered","busy","no_answer","failed","cancelled","expired"],"example":"answered"},"to":{"type":"string","example":"8801712345678","description":"Always returned in canonical form"},"to_display":{"type":"string","example":"01712345678"},"reference":{"type":"string","nullable":true,"example":"order-8842"},"created_at":{"type":"string","format":"date-time"},"dialed_at":{"type":"string","format":"date-time","nullable":true},"ended_at":{"type":"string","format":"date-time","nullable":true},"digit":{"type":"string","nullable":true,"description":"What the customer pressed. `null` when they pressed nothing. A key that was not asked for still comes back as what they pressed."},"connected":{"type":"boolean","description":"Whether the call was handed to an agent or a menu"},"answered_sec":{"type":"integer"},"gather":{"type":"object","nullable":true,"properties":{"digits":{"type":"array","items":{"type":"string"}}}},"webhook":{"type":"object","nullable":true,"properties":{"url":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed"]},"attempts":{"type":"integer"}}}}},"CallList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Call"}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":"integer"}}}}},"AudioList":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":12},"name":{"type":"string","example":"Payment reminder"},"duration_sec":{"type":"integer","example":14},"created_at":{"type":"string","format":"date-time"}}}}}},"Error":{"type":"object","description":"Every error comes back in this same shape — handle it once.","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated","admin_key_forbidden","account_inactive","subscription_inactive","feature_unavailable","scope_required","validation_failed","not_found","not_cancellable","idempotency_conflict","idempotency_key_reused","wrong_host","wrong_account_host","rate_limited","too_many_attempts","request_failed"]},"message":{"type":"string","description":"Human-readable, for a person to read"},"fields":{"type":"object","nullable":true,"description":"Only on `validation_failed`: which field, and what is wrong with it"},"required_scope":{"type":"string","nullable":true,"description":"Only on `scope_required`: which permission was needed"},"base_url":{"type":"string","nullable":true,"description":"Only on `wrong_host`: the correct address"}}}}}},"responses":{"Unauthorized":{"description":"No key, a wrong one, or a revoked one — `unauthenticated`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Not permitted — `scope_required`, `subscription_inactive`, `feature_unavailable`, `admin_key_forbidden`, `wrong_account_host`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"That call is not yours, or does not exist — `not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"`not_cancellable` (the call has started), `idempotency_conflict`, or `idempotency_key_reused` (same key, different request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationFailed":{"description":"Something wrong in the request — `validation_failed`, with `fields`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests, too quickly — `rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}