# DeepChat developer API Send and receive DeepChat messages from your own software. Any DeepChat number can be automated: you do not need a business account, a template approval or a 24-hour reply window. Full reference: [openapi.json](openapi.json). Node and Python clients: [`sdk/node`](../sdk/node), [`sdk/python`](../sdk/python). ## Quickstart (three steps) **1. Get a key.** In the app: Settings, Developer, New test key. The key is shown once. Test keys (`dc_test_...`) use a sandbox and never reach a real phone. Live keys (`dc_live_...`) send for real. **2. Send a message.** ```bash curl -X POST https://api.deepchat.in/v1/messages \ -H "Authorization: Bearer dc_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+919876543210", "text": "Hello from DeepChat" }' ``` ```json { "id": "msg_8f2a1c0d9b3e4a57", "status": "queued" } ``` **3. Receive replies** by registering a webhook: ```bash curl -X PUT https://api.deepchat.in/v1/webhook \ -H "Authorization: Bearer dc_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.example/hook" }' ``` The answer contains your signing `secret` (`whsec_...`). It is shown only once: store it. ## With the Node SDK ```js import DeepChat from 'deepchat'; const dc = new DeepChat(process.env.DEEPCHAT_KEY); await dc.send('+919876543210', 'Hello from DeepChat'); // Express: verify and handle deliveries (use the raw body) app.post('/hook', express.raw({ type: '*/*' }), DeepChat.webhookHandler(process.env.DEEPCHAT_WEBHOOK_SECRET, async (event) => { if (event.event === 'message.received') await dc.send(event.from, `You said: ${event.text}`); })); ``` ## With the Python SDK ```python from deepchat import DeepChat, verify_webhook dc = DeepChat("dc_live_xxx") dc.send("+919876543210", "Hello from DeepChat") # in your web framework's webhook route: event = verify_webhook(WEBHOOK_SECRET, raw_body, request.headers["X-DeepChat-Signature"]) ``` ## Images, documents and buttons **Send a file.** Upload it, then send it by its id: ```bash curl -X POST https://api.deepchat.in/v1/media \ -H "Authorization: Bearer dc_live_xxx" \ -F "file=@invoice.pdf;type=application/pdf" # { "media_id": "med_5c1f...", "name": "invoice.pdf", "mime": "application/pdf", "size": 48211, "expires_at": "..." } curl -X POST https://api.deepchat.in/v1/messages \ -H "Authorization: Bearer dc_live_xxx" -H "Content-Type: application/json" \ -d '{ "to": "+919876543210", "document": { "media_id": "med_5c1f...", "filename": "Invoice 42.pdf", "caption": "Your invoice" } }' ``` Use `"image": { "media_id": ..., "caption": ... }` for pictures: the app shows them in the chat. Files are up to 25 MB, kept encrypted on DeepChat's server for 30 days, and the person's phone downloads them when they choose to. Free plan: 100 MB stored per key and 100 uploads an hour. Photos are not resized for you. **Receive a file.** When a person sends you a file, the `message.received` event has `"type": "media"` and a `media` object (`id`, `name`, `mime`, `size`, `caption`, `url`). Fetch the bytes with `GET /v1/media/{id}` and your key. The first request makes DeepChat fetch the file from the person's phone, which takes a few seconds and needs the phone to be online (otherwise you get `504`: try again later). Always check the type and size before opening a file somebody sent you. **Buttons.** Add up to three `buttons` (1 to 20 characters each, all different) to a text message: ```json { "to": "+919876543210", "text": "Confirm your order?", "buttons": ["Yes", "No"] } ``` When the person taps one, you get a `button.clicked` event; the buttons are then greyed out in their chat: ```json { "event": "button.clicked", "id": "msg_8f2a1c0d9b3e4a57", "from": "+919876543210", "button": "b1", "title": "No", "timestamp": "..." } ``` `id` is the id you got when you sent the message, `button` is `b0`, `b1` or `b2` by position. Buttons are for replies, not links. ## Message statuses | Status | Meaning | |---|---| | `queued` | Accepted. Waiting for the recipient's phone to be reachable. | | `sent` | The relay has it (delivered live or held for the recipient). | | `delivered` | The recipient's phone has it. | | `read` | The recipient read it (they can turn this off). | | `failed` | Could not be delivered. | A status never moves backwards. `GET /v1/messages/{id}` also returns `awaiting_acceptance: true` while the recipient has not accepted you (see below). ## Message requests: how first contact works To stop spam, the first message from any number with API access reaches the person as a **message request** with Accept, Block and Report buttons. Nothing from you appears in their chat list until they accept. - Until they accept, you can send **one** message. A second is refused with `409 awaiting_acceptance`. - If they block you, every send is refused with `403 blocked_by_recipient`. - If they reply to you, that counts as accepting. - Reports count against your account. Enough distinct reports in a week suspend your keys. - Free plan: at most 200 new recipients a day. Write your first message so the person understands who you are and why you are writing. ## Webhooks Three events, delivered as `POST` with a JSON body (`message.received`, `message.status` and `button.clicked`; the last two are shown below the first): ```json { "event": "message.received", "id": "m1", "from": "+919876543210", "type": "text", "text": "Hi!", "timestamp": "2026-10-05T11:30:00Z" } ``` ```json { "event": "message.status", "id": "msg_8f2a1c0d9b3e4a57", "to": "+919876543210", "status": "delivered", "timestamp": "2026-10-05T11:30:02Z" } ``` **Verify every delivery.** The header is `X-DeepChat-Signature: t=,v1=`, where `v1` is the HMAC-SHA256 of `t + "." + the raw body`, keyed with your webhook secret. Reject deliveries older than five minutes. The SDKs do this for you (`DeepChat.verifyWebhook`, `verify_webhook`). **Delivery rules** - Answer with any `2xx` quickly. Anything else is retried after 5 s, 30 s, 5 min, 30 min and 2 h, then dropped. - Events for your account arrive **in order**. While a delivery is being retried, later events wait behind it. - The URL must be `https` and publicly reachable. Addresses inside private networks, the cloud metadata address and redirects are refused. - Delivery can repeat: use the event `id` to ignore repeats. - Events waiting to be retried are kept in memory, so a restart of DeepChat's servers can drop them. Treat the status endpoint as the source of truth. ## Limits | | Free | Starter | |---|---|---| | Requests per second per key | 30 | 100 | | Messages per month | 1,000 | 50,000 | | Text length | 4,000 characters | 4,000 characters | | Contact lookups | 100 an hour | 100 an hour | | File size | 25 MB | 25 MB | | Files kept per key | 100 MB, 30 days | 100 MB, 30 days | | Uploads | 100 an hour | 100 an hour | | Active keys per number | 5 | 5 | Test keys have no monthly cap. Over the limit you get `429` (with `Retry-After` for the per-second limit). ## Errors Errors are JSON: `{ "error": "..." }`. | Status | When | |---|---| | 400 | Bad JSON, number or text; unknown event name; a webhook URL that is not allowed | | 401 | Missing, malformed, revoked or unknown key | | 403 | `blocked_by_recipient`; or a sandbox call with a live key | | 404 | The number is not on DeepChat; no such message | | 409 | `awaiting_acceptance`; or an SDK-mode key used on the hosted endpoint | | 429 | Rate limit, monthly allowance or new-recipient limit | | 413 | A file over 25 MB | | 504 | Downloading a file from a person's phone that is offline | | 507 | The key's file storage allowance is used up | | 502 | DeepChat could not send the message, or could not get a file from the person's phone | ## Hosted mode and your privacy In **hosted mode** (what this guide describes) DeepChat's servers hold the encryption keys for your bot's device. Messages are end-to-end encrypted to the person you write to, but DeepChat can read your side of the conversation. The app shows people that they are talking to a business or bot. If your data is sensitive, wait for **SDK mode**, where the keys stay on your own server. The API does not store your messages: the text is encrypted straight away, and only the encrypted copies wait (until the recipient's phone acknowledges them). Delivery history is not kept. ## What is not available yet - SDK mode (keys on your server, end-to-end encrypted all the way) - An MCP server for AI agents - Java and PHP SDKs - Resizing images for you, and sending a caption with a received file back from the app