Overview
The 247Rep API lets you integrate WhatsApp and email messaging directly into your own products and workflows. It is organised around REST: resources are accessed at predictable URLs, requests use standard HTTP verbs, and every response is JSON.
All requests must be made over HTTPS and authenticated with an API key.
- JSON request and response bodies
- Bearer token authentication with scoped, revocable API keys
- Separate test and live environments
- Cursor-based pagination on all list endpoints
- Per-endpoint rate limiting with standard
X-RateLimit-*headers - Real-time webhooks for inbound and delivery events
Base URL
All API endpoints are relative to the following base URL:
https://api.247rep.app/v1For example, to send a WhatsApp message you would issue a POST request to https://api.247rep.app/v1/messages/send.
Test & live modes
Every workspace has two independent API environments, identified by the key prefix. The environment is inferred from the key you authenticate with — there is no separate header.
| Prefix | Mode | Behaviour |
|---|---|---|
rep_live_… | Live | Sends real messages and deducts real credits. |
rep_test_… | Test | Validates and authenticates exactly like live, but never sends a real message and never deducts credits. Mutating endpoints return a mock response with test: true. |
send and broadcast calls return a synthetic messageId/broadcastId and creditsUsed: 0.API keys
Authenticate every request with a secret API key. Create and revoke keys in your dashboard under Developer API. Each key is scoped to a single workspace and channel, and can be generated for either the test or live environment.
Keys are shown in full only once at creation. 247Rep stores only a SHA-256 hash, so a lost key cannot be recovered — generate a new one instead.
Authorization header
Pass your key as a bearer token in the Authorization header on every request:
Authorization: Bearer rep_live_your_key_hereA request with a missing, malformed, or revoked key returns 401 Unauthorized:
{
"code": "INVALID_KEY",
"message": "Invalid API key"
}Quick start
Send your first WhatsApp message in one request. Replace the key and recipient with your own.
curl -X POST https://api.247rep.app/v1/messages/send \
-H "Authorization: Bearer rep_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"to": "+2348012345678",
"type": "text",
"text": { "body": "Hello from 247Rep!" }
}'const res = await fetch("https://api.247rep.app/v1/messages/send", {
method: "POST",
headers: {
Authorization: "Bearer rep_live_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "+2348012345678",
type: "text",
text: { body: "Hello from 247Rep!" },
}),
});
const data = await res.json();import requests
res = requests.post(
"https://api.247rep.app/v1/messages/send",
headers={"Authorization": "Bearer rep_live_your_key"},
json={
"to": "+2348012345678",
"type": "text",
"text": {"body": "Hello from 247Rep!"},
},
)
data = res.json()Pagination
List endpoints are cursor-paginated. Pass limit (default 50, max 100) and a cursor to fetch the next page. Each response includes hasMore and a nextCursor — pass that value as the cursor on the following request.
Parameters
limitcursor{
"data": [ /* ... items ... */ ],
"hasMore": true,
"nextCursor": "cm5a1b2c3d4e5f6g7h8i9j0k"
}Rate limits
Rate limits are applied per API key and vary by endpoint category. A global burst limit of 20 requests/second also applies across all endpoints.
| Category | Limit | Applies to |
|---|---|---|
| Send | 100 / min | Sending messages, closing conversations, deleting contacts |
| Broadcast | 10 / min | Broadcast sends |
| Contacts (write) | 200 / min | Creating / upserting contacts |
| Read | 300 / min | All GET endpoints |
| Webhooks | 20 / min | Creating / deleting webhooks |
| Burst | 20 / sec | All endpoints (global) |
Every response includes the current limit state:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1718745600When a limit is exceeded the API returns 429 Too Many Requests with a Retry-After header (seconds):
{
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after the specified time."
}Credits & costs
Sending WhatsApp messages consumes credits from your workspace balance. The cost depends on the message type. Read endpoints are free.
| Action | Cost |
|---|---|
| Text message | 2 credits |
| Template message | 4 credits |
| Media message (image, audio, video, document, interactive, location) | 4 credits |
| Broadcast | Per recipient — 2 credits (text) or 4 credits (image/template) |
Send responses include creditsUsed and the remaining creditsRemaining balance. If your balance is too low the API returns 402 Payment Required with code INSUFFICIENT_CREDITS. In test mode creditsUsed is always 0.
Errors
247Rep uses conventional HTTP status codes. 2xx indicates success, 4xx a problem with the request, and 5xx a server error. Every error body has the same shape:
{
"code": "INSUFFICIENT_CREDITS",
"message": "Insufficient credits. Required: 2, available: 0",
"docs": "https://docs.247rep.com/errors#INSUFFICIENT_CREDITS"
}| Code | HTTP | Meaning |
|---|---|---|
INVALID_AUTH | 401 | Missing or malformed Authorization header. |
INVALID_KEY | 401 | The API key does not exist or has the wrong format. |
REVOKED_KEY | 401 | The API key has been revoked. |
FORBIDDEN | 403 | Key not authorized for this resource (e.g. email endpoints require an email channel key). |
INSUFFICIENT_CREDITS | 402 | Workspace balance is too low for this action. |
VALIDATION_ERROR | 400 | The request body failed validation. |
NOT_CONFIGURED | 400 | WhatsApp is not configured for this channel. |
NO_RECIPIENTS | 400 | No valid recipients were found for a broadcast. |
WEBHOOK_VERIFICATION_FAILED | 400 | Your endpoint did not echo the verification challenge. |
NOT_FOUND | 404 | The requested resource does not exist. |
RATE_LIMIT_EXCEEDED | 429 | Too many requests — back off and retry. |
WHATSAPP_ERROR | 502 | Meta's WhatsApp API returned an error. |
INTERNAL_ERROR | 500 | An unexpected error occurred on our side. |
Account
Retrieve information about the workspace and channel tied to your API key.
/v1/accountcurl https://api.247rep.app/v1/account \
-H "Authorization: Bearer rep_live_your_key"Response
{
"workspace": { "name": "Acme Inc", "email": "owner@acme.com" },
"assistant": { "name": "Acme Support", "platform": "WHATSAPP" },
"whatsappNumber": "+2348012345678",
"creditsBalance": 4820,
"environment": "live",
"messagingTier": "TIER_1K",
"qualityRating": "GREEN"
}messagingTier and qualityRating are sourced from Meta and only present when the WhatsApp number is fully connected.Messages
Send WhatsApp messages and read message history.
/v1/messages/sendSend a WhatsApp message to a recipient.
Parameters
totypetextimage / video / documentaudiotemplatelocationcurl -X POST https://api.247rep.app/v1/messages/send \
-H "Authorization: Bearer rep_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"to": "+2348012345678",
"type": "image",
"image": { "link": "https://example.com/photo.jpg", "caption": "Your order" }
}'Response
{
"messageId": "cm5xyz...",
"whatsappMessageId": "wamid.HBgM...",
"status": "sent",
"creditsUsed": 4,
"creditsRemaining": 4816
}/v1/messagesList messages across all conversations, newest first.
Parameters
conversationIddirectionstatuslimitcursor{
"data": [
{
"id": "cm5...",
"conversationId": "cm4...",
"whatsappMessageId": "wamid...",
"direction": "outbound",
"senderType": "api",
"content": "Hello from 247Rep!",
"contentType": "text",
"mediaUrl": null,
"status": "delivered",
"createdAt": "2026-06-19T10:00:00.000Z",
"deliveredAt": "2026-06-19T10:00:02.000Z",
"readAt": null
}
],
"hasMore": false,
"nextCursor": null
}/v1/messages/{messageId}Retrieve a single message by ID, including media metadata and credits used.
Conversations
List and inspect conversations, fetch their messages, and close them.
/v1/conversationsList conversations, newest activity first.
Parameters
statuslimitcursor{
"data": [
{
"id": "cm4...",
"customerPhone": "+2348012345678",
"customerName": "Ada",
"customerEmail": null,
"customerAvatar": null,
"status": "active",
"messageCount": 12,
"lastMessagePreview": "Thanks!",
"lastMessageAt": "2026-06-19T09:58:00.000Z",
"firstMessageAt": "2026-06-10T08:00:00.000Z",
"createdAt": "2026-06-10T08:00:00.000Z",
"serviceWindowOpen": true
}
],
"hasMore": false,
"nextCursor": null
}serviceWindowOpen indicates whether the WhatsApp 24-hour customer service window is still open. When closed, you can only send approved template messages./v1/conversations/{conversationId}Retrieve a single conversation, including automation and escalation state.
/v1/conversations/{conversationId}/messagesList the messages within a conversation, newest first.
/v1/conversations/{conversationId}/closeMark a conversation as closed.
{ "success": true, "conversationId": "cm4...", "status": "closed" }Contacts
Contacts represent customers identified by phone number. Create or update them, list, retrieve, and delete.
/v1/contactsList contacts that have a phone number, newest activity first.
/v1/contactsCreate a contact, or update the existing one with the same phone number (upsert).
Parameters
phonenameemailtagsmetadatacurl -X POST https://api.247rep.app/v1/contacts \
-H "Authorization: Bearer rep_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "phone": "+2348012345678", "name": "Ada Lovelace", "email": "ada@example.com" }'Returns 201 Created for a new contact, or 200 OK when an existing contact was updated.
/v1/contacts/{contactId}Retrieve a single contact.
/v1/contacts/{contactId}Permanently delete a contact and its conversation history.
{ "success": true, "deleted": "cm4..." }Broadcasts
Send a message to many contacts at once. Broadcasts are queued and processed asynchronously.
/v1/broadcastQueue a broadcast to up to 1,000 contacts.
Parameters
contactIdsmessagecurl -X POST https://api.247rep.app/v1/broadcast \
-H "Authorization: Bearer rep_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"contactIds": ["cm4a...", "cm4b..."],
"message": { "type": "text", "text": { "body": "Flash sale today only!" } }
}'Response — 202 Accepted
{
"broadcastId": "cm6...",
"totalRecipients": 2,
"creditsUsed": 4,
"status": "sending"
}/v1/broadcast/{broadcastId}Check the delivery progress of a broadcast.
{
"id": "cm6...",
"message": "Flash sale today only!",
"totalRecipients": 2,
"sentCount": 2,
"failedCount": 0,
"status": "completed",
"createdAt": "2026-06-19T10:00:00.000Z",
"completedAt": "2026-06-19T10:00:30.000Z",
"delivered": 2,
"failed": 0,
"pending": 0
}Templates
List your approved WhatsApp message templates. Templates are required to message a customer outside the 24-hour service window.
/v1/templatesList approved message templates from Meta.
{
"data": [
{
"name": "order_confirmation",
"language": "en",
"category": "UTILITY",
"status": "APPROVED",
"components": [ /* ... */ ]
}
]
}APPROVED templates are returned. Results are cached briefly, so a newly approved template may take a short time to appear.Email — Account
403 FORBIDDEN./v1/email/accountGet the connected email account and sync status.
{
"emailAddress": "support@acme.com",
"displayName": "Acme Support",
"provider": "gmail",
"syncStatus": "active",
"lastSyncAt": "2026-06-19T09:59:00.000Z",
"aiAutoReply": true
}Email — Send
Send an email from your connected account. Sends immediately (no undo window).
/v1/email/sendSend an email message.
Parameters
tosubjectbodyHtmlbodyTextccbccinReplyTothreadIdcurl -X POST https://api.247rep.app/v1/email/send \
-H "Authorization: Bearer rep_live_your_email_key" \
-H "Content-Type: application/json" \
-d '{
"to": "customer@example.com",
"subject": "Your order has shipped",
"bodyHtml": "<p>Tracking: ABC123</p>"
}'Response
{
"messageId": "<api-1718...@247rep.app>",
"threadId": "cm7...",
"status": "sent"
}Email — Threads
List email threads with folder and category filters.
/v1/email/threadsList email threads, newest activity first.
Parameters
foldercategoryisReadlimitcursor{
"data": [
{
"id": "cm7...",
"subject": "Refund request",
"contactEmail": "customer@example.com",
"contactName": "Ada",
"lastMessageAt": "2026-06-19T09:00:00.000Z",
"messageCount": 3,
"isRead": false,
"isStarred": false,
"category": "support",
"priority": "normal"
}
],
"hasMore": false,
"nextCursor": null
}/v1/email/threads/{threadId}Retrieve a single thread with its messages.
Email — Contacts
List unique email contacts, aggregated across threads.
/v1/email/contactsList deduplicated email contacts.
Parameters
searchlimitcursor{
"data": [
{
"email": "customer@example.com",
"name": "Ada",
"lastContactDate": "2026-06-19T09:00:00.000Z",
"messageCount": 5,
"linkedWhatsAppPhone": "+2348012345678"
}
],
"hasMore": false,
"nextCursor": null
}Managing webhooks
Register HTTPS endpoints to receive real-time events. WhatsApp and email each have their own webhook endpoints; use the key for the matching channel.
/v1/webhooksList registered WhatsApp webhooks.
/v1/webhooksRegister a new webhook (verified on creation).
Parameters
urleventssecretGET to your URL with a challenge query parameter. Your endpoint must respond with the exact challenge value as plain text, or registration fails with WEBHOOK_VERIFICATION_FAILED.curl -X POST https://api.247rep.app/v1/webhooks \
-H "Authorization: Bearer rep_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/247rep",
"events": ["message.received", "message.delivered"],
"secret": "whsec_your_secret"
}'{
"id": "cm8...",
"url": "https://yourapp.com/webhooks/247rep",
"events": ["message.received", "message.delivered"],
"isActive": true,
"createdAt": "2026-06-19T10:00:00.000Z"
}/v1/webhooks/{webhookId}Delete a webhook.
Email webhooks
Email channels use the parallel /v1/email/webhooks and /v1/email/webhooks/{webhookId} endpoints with the same request/response shape.
Event types
Subscribe to the events relevant to your integration.
WhatsApp events
| Event | Fired when |
|---|---|
message.received | An inbound message arrives from a customer. |
message.sent | An outbound message is accepted by WhatsApp. |
message.delivered | A message is delivered to the recipient's device. |
message.read | A message is read by the recipient. |
message.failed | A message fails to send or deliver. |
Email events
| Event | Fired when |
|---|---|
email.received | An inbound email arrives. |
email.sent | An outbound email is sent. |
email.opened | A sent email is opened by the recipient. |
email.thread.categorized | The AI assigns a category to a thread. |
Payload
Events are delivered as a JSON POST with a consistent envelope:
{
"event": "message.received",
"timestamp": "2026-06-19T10:00:00.000Z",
"data": {
"conversationId": "cm4...",
"messageId": "wamid...",
"from": "+2348012345678",
"content": "Is this in stock?"
}
}Verifying signatures
When you register a webhook with a secret, every delivery is signed. Verify the X-247Rep-Signature header — an HMAC-SHA256 of the raw request body using your secret — to confirm the request genuinely came from 247Rep.
import crypto from "crypto";
function verifySignature(rawBody: string, signature: string, secret: string) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post("/webhooks/247rep", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.header("X-247Rep-Signature") || "";
if (!verifySignature(req.body.toString(), sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
// handle event...
res.status(200).send("ok");
});2xx status within a few seconds. Do heavy processing asynchronously so deliveries are not retried unnecessarily.