Send a message
Send a free-text WhatsApp message to a candidate, on the recruiter’s behalf.
Headers
X-Api-Keyonb_live_<key_id>_<secret>required- Your issued Partner API key — see Authentication.
Idempotency-Key<your own retry id>optional- Optional. Send it and a retry of the same request replays the first outcome instead of repeating the write. 1–255 characters. Per account, per route, per candidate.
Request fields
bodystringrequired- Non-empty, ≤2000 characters — the same cap the dashboard’s own reply composer is sized for.
Status codes
4024xx- Your plan has ended (
code: "no_subscription"), or this month’s messages are used up and there is no open 24-hour chat with this candidate (code: "over_message_quota"). Nothing was sent. A reply inside an open chat is never refused for the message limit. 5035xx- We could not check your plan just now (
code: "billing_unavailable"). Nothing was sent; retry shortly. 2002xx- Sent. Check delivery later via Retrieve messages, keyed on
message_id— occasionallynull(see notes below); the message was still sent either way. With anIdempotency-Keyalready used for this candidate, this is the ORIGINAL answer again withreplayed: trueinsidedata— nothing is sent a second time. 4004xx- Missing/empty
body, over the length cap, or anIdempotency-Keyover the length limit.A non-empty `body` is required`body` exceeds the maximum length of {max} charactersThe Idempotency-Key header must be at most 255 characters. 4014xx- Missing, malformed, unknown, revoked or expired key. Every authentication failure looks the same — see Authentication.
4034xx- The key’s user has the
viewerteam role, or their account could not be read just then (retry) — or the key was created Read only (code: "key_read_only", refused before anything else). Checked before the body or any id — see Authentication.Your team role doesn't allow this. Ask your team's admin or owner.This API key is read-only. Create a full-access key to make changes. 4044xx- Unknown, cross-tenant, or sandbox-hidden. One outcome, never a 403 — even one this route’s own send seam would otherwise return.
Not found 4094xx- Consent not granted, the candidate cannot be messaged right now (opted out, data processing paused, messaging disabled, account scheduled for deletion, or the WhatsApp session window has closed — see Send a template for a compliant fallback), or another request with this
Idempotency-Keyis still in flight.This candidate has opted out of messages.This candidate's data processing is paused — a message cannot be sent.Messaging is currently disabled for this candidate.This account is scheduled for deletion — a message cannot be sent.This candidate hasn't replied on WhatsApp in over 24 hours — a free-form message can't be sent outside the messaging window.This candidate has received too many messages recently — try again shortly.This candidate's status could not be verified. Try again shortly.This message could not be sent. Try again shortly.A request with this Idempotency-Key is already in progress. 4294xx- Per-key/per-account rate limit, OR this candidate’s own outbound message throttle.
Too many requests — please try again shortly. 5005xx- The service is temporarily unavailable.
This resource is temporarily unavailable. Try again shortly. 5025xx- The send transport failed.
This message could not be sent. Try again shortly. 5035xx- This candidate’s status could not be verified — retry.
This candidate's status could not be verified. Try again shortly.
Delivery status lives on Retrieve messages
This response only confirms the send was accepted. Poll GET /api/v1/candidates/{id}/messages and match message_id against id in that page’s messages array for delivery_status/delivery_error.
- Sends VERBATIM text — never an approved template. Outside the 24-hour WhatsApp session window, this endpoint refuses (409) rather than falling back to a template; see Send a template for the compliant door onto a candidate who has gone quiet.
- The candidate must have granted consent — a candidate mid-onboarding who has not yet consented cannot be messaged this way.
- Sending or pausing does not require the bot to be paused first — but see Pause or resume the bot if you want the bot to stop replying while your own recruiter is handling the thread.
message_idcan benullon a200— the WhatsApp message was still sent; this only means the row backing it could not be resolved yet. Fall back to matchingsent_atandbodyon Retrieve messages in that case.
cURL
curl -X POST \ "https://rca-onboardly.com/api/v1/candidates/<candidate id>/messages" \ -H "X-Api-Key: $ONB_KEY" \ -H "Idempotency-Key: <your own retry id>" \ -H "Content-Type: application/json" \ -d '{ "body": "Thanks for sending your DBS — we just need Proof of Address next."}'Request body
{ "body": "Thanks for sending your DBS — we just need Proof of Address next."}200 · Response
{ "message_id": "b2f4c1a0-7e3d-4a9c-8b1f-2d6e5a9c3f70", "delivery_status": "sent", "sent_at": "2026-09-18T10:14:02Z"}Delivery status is not returned here — poll Retrieve messages and match message_id.
POST /api/v1/candidates/{id}/messages
cURL
curl -X POST \ "https://rca-onboardly.com/api/v1/candidates/<candidate id>/messages" \ -H "X-Api-Key: $ONB_KEY" \ -H "Idempotency-Key: <your own retry id>" \ -H "Content-Type: application/json" \ -d '{ "body": "Thanks for sending your DBS — we just need Proof of Address next."}'Request body
{ "body": "Thanks for sending your DBS — we just need Proof of Address next."}200 · Response
{ "message_id": "b2f4c1a0-7e3d-4a9c-8b1f-2d6e5a9c3f70", "delivery_status": "sent", "sent_at": "2026-09-18T10:14:02Z"}