Sending Messages
Every send is a single POST to one of four endpoints, authenticated with your instance's API
key. Each returns the same shape — a message id you can later check the status of.
Authorization: Bearer sk_<your instance api key>
Content-Type: application/json
Every response looks like:
{ "id": "83ad449d-c6ef-49c0-97be-c08b5e452d73" }
One outbound send — text, image, location, or contact card — costs exactly one message credit (prepaid mode) or counts toward your plan (fixed mode). Inbound messages and webhook deliveries are never billed. See Billing.
Text
curl -X POST https://app.tarceel.com/instances/<instanceId>/messages/text \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{"to": "15551234567", "text": "Hello from Tarceel!"}'
to is a phone number — country code + number, digits only (no +, no spaces, no dashes).
Media
Image, video, audio, or document — either a public URL or raw base64:
curl -X POST https://app.tarceel.com/instances/<instanceId>/messages/media \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"to": "15551234567",
"type": "image",
"url": "https://example.com/photo.jpg",
"caption": "Your receipt"
}'
Send exactly one of url or base64 — not both. mimetype is required when type is
document; filename and caption are optional on any media type.
Location
curl -X POST https://app.tarceel.com/instances/<instanceId>/messages/location \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{"to": "15551234567", "latitude": 24.8607, "longitude": 67.0011, "name": "Our office"}'
name and address are optional labels shown alongside the pin.
Contact card
curl -X POST https://app.tarceel.com/instances/<instanceId>/messages/contact \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{"to": "15551234567", "fullName": "Jane Doe", "phone": "15559876543"}'
Checking delivery status
curl https://app.tarceel.com/instances/<instanceId>/messages/<id>/status \
-H "Authorization: Bearer sk_..."
{ "id": "83ad449d-...", "status": "delivered" }
status moves through pending → sent → delivered → read, or failed. Polling works, but
if you're sending any real volume, subscribe to the message.status webhook
event instead — it fires the moment each transition happens.
Errors you'll actually see
| Status | Meaning |
|---|---|
400 | Bad request — missing/invalid field. |
402 | Payment required — the instance has no credits left, or its subscription isn't active. See Billing. |
409 | The instance's WhatsApp session isn't connected right now (needs a QR scan, or was paused). |
Try it without writing any code
Every instance's detail page in the dashboard has a "Try the API" tab: pick an operation, fill in a small form, and it shows the equivalent request in cURL, Node.js, Python, PHP, Java, and C#/.NET — with that instance's real id and key already filled in. A Send test request button fires it for real and shows you the raw response before you write a single line of your own code.