Send a location pin
POSThttps://api.wapito.com/v1/messages/location
Send a channel token on every request: Authorization: Bearer wpt_YOUR_TOKEN
Sends a static location pin with an optional place name and street address, which the recipient can open in their maps app or use for directions. Coordinates are decimal degrees. This is a one-off pin rather than live location sharing, which WhatsApp only offers from the app and no unofficial engine can start on your behalf.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
address | string | Optional | Street address shown under the place name. |
latitude | number | Required | Decimal degrees, -90 to 90.-90–90 |
longitude | number | Required | Decimal degrees, -180 to 180.-180–180 |
name | string | Optional | Place name shown in the bubble. |
quoted | string | Optional | Id of a message in the same chat to reply to. |
to | string | Required | A WhatsApp recipient. Accepted forms: `+E.164` (`+15551234567`), bare digits (`15551234567`), `<digits>@s.whatsapp.net`, `<digits>@c.us`, `<group id>@g.us`, `<lid>@lid` and `<channel id>@newsletter`. Phone forms are validated with libphonenumber and rejected with `invalid_recipient` when they are not dialable. Wapito normalises the value before it reaches the engine and always returns `@s.whatsapp.net` ids. |
{
"address": "14 Forge Lane, Sheffield S3 8GG",
"latitude": 53.3811,
"longitude": -1.4701,
"name": "Acme Supplies",
"to": "+15551234567"
}Responses
201
The message was accepted and queued on the per-channel send queue. Delivery receipts arrive later as `messages.status` events.
| Field | Type | Required | Description |
|---|---|---|---|
audio | object | Optional | Media attached to a message. `link` is a signed Wapito URL, not a WhatsApp CDN URL. |
caption | string | null | Required | Caption sent alongside the media. |
error | string | Optional | Set to `unavailable` when the media could not be copied before it expired on the engine.one of unavailable |
file_name | string | null | Required | Original file name when the sender supplied one. |
file_size | integer | null | Required | Size in bytes. |
id | string | null | Required | Wapito media id (`med_` + ULID), or `null` when the copy from the engine failed. |
link | string | null | Required | Signed `/v1/media/{id}?exp&sig` URL, valid 24 h on sandbox and 7 days on premium. |
mime_type | string | Required | IANA media type reported by WhatsApp. |
chat_id | string | Required | Chat the message belongs to, as `@s.whatsapp.net`, `@g.us` or `@newsletter`. |
contact | object | Optional | Contact card payload of a `contact` message. |
vcard | string | Required | Raw vCard 3.0 text as WhatsApp stores it. |
context | object | Optional | Reply, forward and mention context for a message. |
forwarded | boolean | Required | Whether WhatsApp marked the message as forwarded. |
mentions | array of string | Required | Phone numbers mentioned with @ in the body. |
quoted_author | string | null | Required | Sender of the quoted message. |
quoted_id | string | null | Required | Id of the quoted message, when this message is a reply. |
document | object | Optional | Media attached to a message. `link` is a signed Wapito URL, not a WhatsApp CDN URL. |
caption | string | null | Required | Caption sent alongside the media. |
error | string | Optional | Set to `unavailable` when the media could not be copied before it expired on the engine.one of unavailable |
file_name | string | null | Required | Original file name when the sender supplied one. |
file_size | integer | null | Required | Size in bytes. |
id | string | null | Required | Wapito media id (`med_` + ULID), or `null` when the copy from the engine failed. |
link | string | null | Required | Signed `/v1/media/{id}?exp&sig` URL, valid 24 h on sandbox and 7 days on premium. |
mime_type | string | Required | IANA media type reported by WhatsApp. |
from | string | null | Required | Sender in digits for direct chats, or the group id for group chats. `null` when only a LID is known. |
from_lid | string | null | Required | Sender LID when WhatsApp addressed the message by LID rather than phone number. |
from_me | boolean | Required | True when the channel sent the message, false when it received it. |
from_name | string | null | Required | Push name of the sender as WhatsApp reports it. |
id | string | Required | Engine message id, unique within the channel. |
image | object | Optional | Media attached to a message. `link` is a signed Wapito URL, not a WhatsApp CDN URL. |
caption | string | null | Required | Caption sent alongside the media. |
error | string | Optional | Set to `unavailable` when the media could not be copied before it expired on the engine.one of unavailable |
file_name | string | null | Required | Original file name when the sender supplied one. |
file_size | integer | null | Required | Size in bytes. |
id | string | null | Required | Wapito media id (`med_` + ULID), or `null` when the copy from the engine failed. |
link | string | null | Required | Signed `/v1/media/{id}?exp&sig` URL, valid 24 h on sandbox and 7 days on premium. |
mime_type | string | Required | IANA media type reported by WhatsApp. |
location | object | Optional | Location payload of a `location` message. |
address | string | null | Required | Street address shown under the place name. |
latitude | number | Required | Decimal degrees. |
longitude | number | Required | Decimal degrees. |
name | string | null | Required | Place name shown in the bubble. |
participant | string | null | Required | In group chats, the individual sender behind `from`. |
poll | object | Optional | Poll payload of a `poll` message. Votes arrive later as `polls` webhook events. |
multiple | boolean | Required | Whether voters may select more than one option. |
options | array of string | Required | Answer options in display order. |
title | string | Required | Poll question. |
raw | object | Optional | Untouched engine payload. Present only while `settings.include_raw` is enabled. |
source | string | Required | `app` when the message came from the linked phone, `api` when Wapito sent it.one of app, api |
status | string | Optional | Delivery state of an outbound message.one of failed, pending, sent, delivered, read, played |
sticker | object | Optional | Media attached to a message. `link` is a signed Wapito URL, not a WhatsApp CDN URL. |
caption | string | null | Required | Caption sent alongside the media. |
error | string | Optional | Set to `unavailable` when the media could not be copied before it expired on the engine.one of unavailable |
file_name | string | null | Required | Original file name when the sender supplied one. |
file_size | integer | null | Required | Size in bytes. |
id | string | null | Required | Wapito media id (`med_` + ULID), or `null` when the copy from the engine failed. |
link | string | null | Required | Signed `/v1/media/{id}?exp&sig` URL, valid 24 h on sandbox and 7 days on premium. |
mime_type | string | Required | IANA media type reported by WhatsApp. |
text | object | Optional | Body of a `text` message. |
body | string | Required | Message text, WhatsApp markdown allowed. |
timestamp | integer | Required | Unix epoch milliseconds. |
type | string | Required | Message kind; selects which payload field is populated.one of text, image, video, audio, voice, document, sticker, location, contact, poll, reaction, unknown |
video | object | Optional | Media attached to a message. `link` is a signed Wapito URL, not a WhatsApp CDN URL. |
caption | string | null | Required | Caption sent alongside the media. |
error | string | Optional | Set to `unavailable` when the media could not be copied before it expired on the engine.one of unavailable |
file_name | string | null | Required | Original file name when the sender supplied one. |
file_size | integer | null | Required | Size in bytes. |
id | string | null | Required | Wapito media id (`med_` + ULID), or `null` when the copy from the engine failed. |
link | string | null | Required | Signed `/v1/media/{id}?exp&sig` URL, valid 24 h on sandbox and 7 days on premium. |
mime_type | string | Required | IANA media type reported by WhatsApp. |
voice | object | Optional | Media attached to a message. `link` is a signed Wapito URL, not a WhatsApp CDN URL. |
caption | string | null | Required | Caption sent alongside the media. |
error | string | Optional | Set to `unavailable` when the media could not be copied before it expired on the engine.one of unavailable |
file_name | string | null | Required | Original file name when the sender supplied one. |
file_size | integer | null | Required | Size in bytes. |
id | string | null | Required | Wapito media id (`med_` + ULID), or `null` when the copy from the engine failed. |
link | string | null | Required | Signed `/v1/media/{id}?exp&sig` URL, valid 24 h on sandbox and 7 days on premium. |
mime_type | string | Required | IANA media type reported by WhatsApp. |
{
"chat_id": "15551234567@s.whatsapp.net",
"context": {
"forwarded": false,
"mentions": [],
"quoted_author": null,
"quoted_id": null
},
"from": "15557654321",
"from_lid": null,
"from_me": true,
"from_name": "Acme Support",
"id": "true_15551234567@s.whatsapp.net_3EB0C767D82B0A1E4F2B",
"location": {
"address": "14 Forge Lane, Sheffield S3 8GG",
"latitude": 53.3811,
"longitude": -1.4701,
"name": "Acme Supplies"
},
"participant": null,
"source": "api",
"status": "sent",
"timestamp": 1789459200000,
"type": "location"
}400
The request body or query string is malformed, or the recipient cannot be parsed into a WhatsApp id.
Show 2 example bodies
invalid_recipient — `to` is not a dialable number or valid WhatsApp id
{
"error": {
"code": "invalid_recipient",
"details": {
"to": "+1555"
},
"message": "The recipient is not a valid WhatsApp address.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}invalid_request — schema validation failed
{
"error": {
"code": "invalid_request",
"details": {
"issues": [
{
"message": "Array must contain at least 2 element(s)",
"path": "body.options"
}
]
},
"message": "The request payload failed validation.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}401
The channel token is missing, malformed, revoked or belongs to a deleted channel.
Show 2 example bodies
token_revoked — the token was rotated in the dashboard
{
"error": {
"code": "token_revoked",
"message": "This channel token has been revoked.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}unauthorized — no or unusable Bearer token
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid channel token.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}403
The token is valid but the channel may not perform this action right now.
Show 2 example bodies
channel_locked — billing lapsed or the channel was locked by an operator
{
"error": {
"code": "channel_locked",
"details": {
"reason": "plan_required"
},
"message": "This channel is locked.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}forbidden — the channel does not own the target object
{
"error": {
"code": "forbidden",
"message": "You are not allowed to perform this action.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}409
The channel is in the wrong state for this action, or the account type does not support it.
Show 3 example bodies
business_account_required — labels need WhatsApp Business
{
"error": {
"code": "business_account_required",
"message": "This action requires a WhatsApp Business account.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}channel_not_connected — link the number first
{
"error": {
"code": "channel_not_connected",
"details": {
"status": "qr"
},
"message": "The channel is not connected.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}channel_not_in_qr_state — no QR available while the session boots
{
"error": {
"code": "channel_not_in_qr_state",
"details": {
"status": "created"
},
"message": "The channel is not waiting for a QR scan.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}423
WhatsApp has temporarily blocked outbound first contact from this number.
Show the example body
reachout_timelocked — wait until `details.until`
{
"error": {
"code": "reachout_timelocked",
"details": {
"until": "2026-09-15T14:00:00.000Z"
},
"message": "WhatsApp has temporarily locked reach-outs for this channel.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}429
A rate limit, a plan quota or one of the anti-ban guards stopped the request. Every one of these is safe to retry later; read `Retry-After` when present.
Show 5 example bodies
cold_send_limit — too many first messages to new recipients this hour
{
"error": {
"code": "cold_send_limit",
"details": {
"resets_at": "2026-09-15T09:00:00.000Z",
"window_cap": 20
},
"message": "The hourly limit for messages to new recipients is reached.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}quota_exceeded — plan quota for the day or month
{
"error": {
"code": "quota_exceeded",
"details": {
"quota": "sent",
"resets_at": "2026-09-16T00:00:00.000Z",
"used": 150
},
"message": "The plan quota for this resource is exhausted.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}rate_limited — per-minute API rate limit
{
"error": {
"code": "rate_limited",
"details": {
"retry_after": 12
},
"message": "Too many requests.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}send_rate_limited — the send queue did not drain within 20 s
{
"error": {
"code": "send_rate_limited",
"details": {
"retry_after": 5
},
"message": "The send queue for this channel is saturated.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}warmup_limit — the warm-up ladder cap for today
{
"error": {
"code": "warmup_limit",
"details": {
"cap": 200,
"day": 2
},
"message": "The warm-up limit for this channel is reached.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}500
Something went wrong inside Wapito. Quote `request_id` when reporting it.
Show the example body
internal_error — unexpected failure
{
"error": {
"code": "internal_error",
"message": "Something went wrong on our side.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}502
The engine answered with an error Wapito could not translate into a more specific code.
Show the example body
engine_error — unexpected engine failure
{
"error": {
"code": "engine_error",
"details": {
"engine": "gows",
"status": 500
},
"message": "The WhatsApp engine returned an error.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}503
The engine session is not reachable. Retry with backoff.
Show the example body
engine_unavailable — engine down or restarting
{
"error": {
"code": "engine_unavailable",
"details": {
"engine": "gows"
},
"message": "The WhatsApp engine is unavailable.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}504
The engine did not answer before the upstream timeout.
Show the example body
engine_timeout — no answer within the deadline
{
"error": {
"code": "engine_timeout",
"details": {
"timeout_ms": 20000
},
"message": "The WhatsApp engine did not respond in time.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}Webhook events
- messagesMessage sent or received
onMessages() - messages.statusDelivery receipt for a sent message
onMessagesStatus()
Errors
Code examples
import requests
url = "https://api.wapito.com/v1/messages/location"
payload = {
"to": "+15551234567",
"latitude": 53.3811,
"longitude": -1.4701,
"name": "Acme Supplies",
"address": "14 Forge Lane, Sheffield S3 8GG"
}
headers = {
"Authorization": "Bearer wpt_YOUR_TOKEN",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())Used in
Related
Try it on your own number
Create a channel, link a WhatsApp number by QR or pairing code, and call the API in a couple of minutes. The Sandbox plan is free and needs no card.