Choose scopes and copy the keyTick messages:send for this example. The key is shown only once, so store it somewhere safe.
Send a messageSet the key as an environment variable and make the request.
Terminal
export REPEATGROW_API_KEY="RG-XXXX-..."
# Check the key works
curl https://repeatgrow.com/api/v1/me -H "Authorization: Bearer $REPEATGROW_API_KEY"
curl -X POST https://repeatgrow.com/api/v1/messages \
-H "Authorization: Bearer $REPEATGROW_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "to": "+14155550123", "text": "Hi 👋 your order has shipped." }'
WhatsApp only delivers free-form text and media within 24 hours of the customer's last message. To start a new conversation, send an approved template.
02Authentication
Send your key as a bearer token on every request: Authorization: Bearer RG-XXXX-…
Each key belongs to one account and can only do what its scopes allow.
Repeat Grow stores only a hash, so a lost key can't be shown again. Revoke it and create a new one in repeatgrow.com/settings?tab=api.
Keep keys on your server. Never put them in a website or mobile app.
Scope
Allows
messages:send
Send WhatsApp messages
messages:read
Read messages and their delivery status
contacts:read
List and read contacts
contacts:write
Create and update contacts
conversations:read
List and read conversations
broadcasts:send
Launch broadcast campaigns
webhooks:manage
Register and manage outbound event webhooks
templates:read
List message templates and their review status
templates:write
Create message templates and submit them to Meta
A key with no scopes can still call GET /me to check it works.
03Errors & rate limits
Every response uses one of two shapes. Branch on error.code; the message is for people and may change.
Response shapes
// Success
{ "data": { ... } }
// Error
{ "error": { "code": "forbidden", "message": "This API key is missing the 'messages:send' scope" } }
Status
Code
Meaning
400
bad_request
Malformed input. The message says which field.
400
whatsapp_not_configured
Connect WhatsApp in Settings first.
401
unauthorized
Missing, invalid, revoked, or expired key.
403
forbidden
The key doesn't have the scope this endpoint needs.
404
not_found
No such resource in your account.
429
rate_limited
Over 120 requests a minute. Wait Retry-After seconds.
500
internal
Server error. Safe to retry with backoff.
502
meta_error
WhatsApp rejected the send, e.g. outside the 24-hour window.
Each key can make 120 requests per minute. A 429 response includes Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
04Pagination
List endpoints return up to limit items (default 50, max 100) and a meta.next_cursor. Pass it back as cursor for the next page. When it's null, you've reached the end.
Send a message to a phone number in E.164 format. The contact and conversation are found or created automatically. Free-form messages (text/media) only deliver inside WhatsApp's 24-hour customer-service window; outside it, send an approved template.
Request
curl -X POST "https://repeatgrow.com/api/v1/messages" \
-H "Authorization: Bearer $REPEATGROW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+14155550123",
"type": "text",
"text": "Hi 👋 your order has shipped."
}'
Sends an approved template to up to 1,000 recipients per request. Returns immediately while sends run in the background — poll GET /broadcasts/{id} for progress. Invalid numbers and contacts who replied STOP (opted out) are dropped and counted as rejected.
Submits the template to Meta for review. Safe to call again: the same name, language and wording returns the current template with 200 and nothing is resubmitted; a REJECTED or DRAFT template is resubmitted; an approved or pending template with different wording returns 409 conflict (use a new name). AUTHENTICATION templates are not supported.
For partner apps: exchange a "Connect RepeatGrow" code for a key, and disconnect.
POST/api/v1/connect/token
Exchange a Connect code for an API key
Server to server, for registered partner apps only. After the account owner clicks Allow on /connect/{client}, exchange the one-time code (valid 5 minutes, single use) for a scoped API key. Authenticates with the client secret, not an API key. The key is returned once and never shown again.
Each delivery has an X-Wacrm-Signature: t=<unix>,v1=<hex> header, where v1 is an HMAC-SHA256 of {t}.{raw body} using the whsec_… secret you got when creating the endpoint. Events also carry X-Wacrm-Event and X-Wacrm-Webhook-Id.
import crypto from "node:crypto";
// Pass the RAW request body (a string), not re-serialized JSON.
export function verifyRepeatGrowSignature(rawBody, header, secret) {
const match = /t=(\d+),v1=([0-9a-f]+)/.exec(header ?? "");
if (!match) return false;
const [, t, v1] = match;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay guard
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
Reply with any 2xx quickly. Each event is sent once with a short timeout, and redirects aren't followed.
Events can repeat or arrive out of order, so dedupe on id.
After repeated failures an endpoint is switched off. Turn it back on with PATCH and { "is_active": true }.
The URL must be public HTTPS. Private and local addresses are refused.
07OpenAPI spec
The full API is described in a public OpenAPI 3.1 file at https://repeatgrow.com/api/v1/openapi.json. Import it into Postman, Insomnia, or Swagger UI, or generate a typed client.