Your first API call
Mint a key, verify it, and send a real WhatsApp message from your own backend.
The API lets your own systems do what your team does in the console: send a message, place a call, read a conversation, open a ticket. This page takes you from nothing to a real message delivered.
Server-side only
/api/v1 sends no CORS headers, by design. An API key in front-end JavaScript is a leaked API key — anyone can read it out of your bundle. Call this API from your backend. For in-page use, choose the hosted widget or custom-UI client; both use origin-bound visitor sessions instead of API keys.
1. Create a key #
You need the owner or admin role. Keys are available on every plan, but this page sends a WhatsApp message, so you also need a connected WhatsApp number (Starter or above) and an approved template.
- 1
Open API keys
In the console, open Developers. It opens on the API keys tab. Click Create key (or Create your first key).
- 2
Name it after the system that will hold it
Order service (production), notTest key 2. When a key starts behaving strangely at 3 a.m., the name is what tells you which deployment to look at. - 3
Choose its scopes now
Under What this key can do, tick what the integration needs. Read conversations and Read agents start ticked. For this page, also tick Send messages (
messages:send). Untick anything the integration won't use.A key's scopes can't be changed after it is created. To widen or narrow a key later, create a new key with the right scopes, deploy it, and revoke the old one. See Change what a key can do and Scopes.
- 4
Copy the key
Click Create key. The Copy your API key dialog shows the full key once. Put it in your secret manager, tick I've copied it somewhere safe, and click Done.
The secret is shown exactly once
We store a hash, not the key. Nobody at Amomic-AI can recover it for you. If you lose it, Roll the key: you get a new secret with the same name and scopes, and the old one keeps working for 24 hours so you can deploy without downtime.
2. Verify it #
GET /ping costs nothing, changes nothing, and tells you everything about how the API sees your key. It should be the first call every integration makes.
curl https://services.amomic.in/api/v1/ping \-H "Authorization: Bearer $AMOMIC_API_KEY"
{"ok": true,"workspace_id": "ten_01JQRS4T8XZ0AB1CD2EF3GH4","key_name": "Order service","environment": "live","scopes": ["agents:read", "conversations:read", "messages:send"]}
Read scopes carefully. Most "why is this 403" questions are answered right here, before any real request is made.
3. Find out what your workspace can do #
Ids don't exist in a vacuum — you need a sender to send from. GET /channels exists precisely because a backend integrator has no console open to copy one out of.
curl https://services.amomic.in/api/v1/channels \-H "Authorization: Bearer $AMOMIC_API_KEY"
{"message_channels": ["whatsapp"],"call_channels": ["voice"],"senders": [{"channel": "whatsapp","sender_id": "was_9f2c41ab77de1063c8a4b512","display_number": "+919461093306","verified_name": "Acme Retail","is_default": true,"status": "connected"}]}
4. Send a real message #
Now the interesting one. This sends an actual WhatsApp message to an actual phone.
curl -X POST https://services.amomic.in/api/v1/messages \-H "Authorization: Bearer $AMOMIC_API_KEY" \-H "Content-Type: application/json" \-H "Idempotency-Key: order-4821-shipped" \-d '{"to": "+919876543210","template": {"name": "order_shipped","language": "en_US","variables": ["Asha", "AM-4821"]}}'
{"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgSMkI5RUE4...","channel": "whatsapp","to": "+919876543210","status": "sent"}
Your order is on the way
Hi Asha, your order AM-4821 has been shipped and should reach you by 6 PM today.
Reply STOP to unsubscribe
Rendered with the same rules WhatsApp applies — what you see is what your customer gets.
Three things in that request are doing real work:
templaterather thantext. You can only send free-form text inside the 24-hour window that opens when a customer messages you. An order update almost never falls inside it. The 24-hour window explains when each applies.Idempotency-Key. Your retry after a network timeout replays the stored response instead of messaging the customer twice. See Idempotency.variablespositionally.["Asha", "AM-4821"]fills{{1}}and{{2}}. An object like{"1": "Asha", "2": "AM-4821"}works too.
5. Handle the failures properly #
Every error uses the same envelope, so one handler covers the whole API:
{"error": {"code": "failed_precondition","message": "This workspace has no WhatsApp sender connected.","details": { "reason": "channel_not_connected" },"request_id": "req_01JQRS4T8XZ0AB1CD2EF3GH4","correlation_id": "cor_01JQRS4T8XZ0AB1CD2EF3GH4"}}
Branch on error.code for the class of problem and on error.details.reason for the specific cause. Log request_id — quoting it to support turns a support thread into a lookup.
The ones you will actually hit first:
unauthenticatedwrong_environmentA test key was used against production, or the reverse. Not a bad secret.permission_deniedmissing_scopeThe key is valid but lacks the scope. `details.required_scope` names it.not_foundtemplate_not_foundNo approved template by that name. Check spelling and approval status.conflictsession_window_closedFree-form text outside the 24-hour window. Send a template.failed_preconditionchannel_not_connectedNo WhatsApp sender is connected to this workspace yet.Errors has the complete list.
Test keys and live keys #
There is one production API. Keys carry an environment and are checked against the host they are used on:
amk_live_…live keyoptionalWorks against services.amomic.in. Real messages, real calls, real money.
amk_test_…test keyoptionalIssued in non-production environments. Sending one at the production host returns 401 with wrong_environment.
A mismatch is always a 401 with details.reason of wrong_environment — never a silent failure and never a bad-secret error. If you see it, you have the right key pointed at the wrong host.
Next #
Place an AI call
POST /calls has your agent phone someone and talk to them.
Receive events
Webhooks push replies, call outcomes, and new tickets to you instead of you polling.
A complete flow
Order-shipped update — the whole thing end to end, including retries and the webhook that closes the loop.
Hand it to a coding agent
Use with agents connects these docs to Claude Code or Codex so it writes the integration with the real contract in front of it.