Rate limiting
BlueSender enforces several rate limits that an integration may observe. The two below are the ones a typical CRM message-routing flow will hit; additional limiters apply to provisioning and OAuth endpoints (see Other rate limits below).
1. HTTP webhook rate limit (per locationId)
The BlueSender backend caps the rate of inbound HTTP webhook calls
per locationId. This protects the backend from a misbehaving CRM
flooding a single tenant.
Keying. The limiter keys on the request body's top-level
locationId only when it is a valid 3–200 character alphanumeric
string. Requests with a missing, malformed, or out-of-range value
fall back to IP-based keying instead — meaning multiple
unrelated tenants behind the same source IP can share the same
limiter bucket. Send a valid locationId on every request to avoid
this collision. The format check is on by default and can be
disabled by an operator with
RATE_LIMIT_VALIDATE_LOCATION_ID=false (any other value, including
unset, leaves the check enabled).
Trigger and response
| Parameter | Default |
|---|---|
| Window | 1 minute (fixed) |
| Max requests per window | 60 / locationId / minute (tunable per deployment via RATE_LIMIT_CRM_WEBHOOK) |
| Action on exceeded | Reject with HTTP 429 |
When the limit is exceeded, the webhook handler does not run — the request is rejected at the rate-limiter layer with:
{
"error": "TOO_MANY_REQUESTS",
"message": "Too many webhook requests for this location, please try again later."
}
The RateLimit-* standard headers are also returned, so clients that
read them can pace themselves automatically.
Recommended client behavior
- On
HTTP 429, back off and retry. Honor theRetry-After/RateLimit-Resetheaders if your HTTP client surfaces them. - For burst-prone workloads, apply your own client-side rate limit below the configured cap.
- Coordinate with the BlueSender operator if your integration's sustained rate exceeds the default — the cap is tunable and raising it for a known integration is preferable to a tight retry loop.
2. Mac automation-queue limit
Once a webhook is forwarded to the Mac, every outbound send goes
through a single FIFO automation queue with a fixed depth (500
pending jobs across all message types). When the queue is full, the
message is rejected and the failure is reported back to the CRM
through the standard status-update channel (PUT /conversations/messages/{messageId}/status):
{
"status": "failed",
"error": {
"code": "RATE_LIMITED",
"type": "error",
"message": "Automation queue full; dropped to prevent unbounded backlog"
}
}
The queue covers all outbound work:
- Plain text messages.
- Messages with attachments or animation effects.
- Outbound reactions (Tapbacks) — see Reactions: Outbound.
- Outbound standalone stickers — see Stickers: Outbound.
In practice, plain text sends drain quickly enough that you'll
rarely see RATE_LIMITED on a text-only workload — but the path
exists, so any sustained burst at high enough volume can trip the
500-job cap.
Recommended client behavior
- Treat
RATE_LIMITEDas a transient, retryable failure. - Back off briefly (~30 seconds is reasonable — the queue drains as the Mac completes work) before re-sending.
- For burst-prone workloads, apply your own client-side rate limit at ~50 ops/minute on the affected message types.
- Don't retry indefinitely on
RATE_LIMITEDfor the samemessageId— if the user is no longer in the target chat, the operation may simply be stale by the time it could drain.
When both apply
Heavy CRM-initiated workloads can hit either limit:
- A flood of
POST /crm/webhookcalls hits the HTTP webhook rate limit (HTTP 429 +TOO_MANY_REQUESTS). - A flood of sends that drain slowly on the Mac — most often
attachments, animation effects, reactions, and standalone stickers,
but plain text counts too — can fill the automation queue
(status callback with
error.code: RATE_LIMITED).
The two limits are independent: hitting one does not exempt you from the other.
Other rate limits
These don't fire on the typical CRM message-routing flow but are visible to marketplace OAuth callbacks and infrastructure probes:
| Surface | Endpoints | Limit | Action on exceeded |
|---|---|---|---|
| OAuth callback | GET /oauth/callback/crm, POST /oauth/callback/crm (CRM marketplace install flow) | tunable per deployment | HTTP 429 with a JSON body. |
| Health | GET /health | 30 / IP / minute | HTTP 429 with a JSON body. |
| Health (PM2 probe) | GET /health?pm2=1 | 5 / IP / minute | HTTP 429 with a JSON body. |
If you build a marketplace integration, plan for HTTP 429 on the
OAuth callback the same way you do for /crm/webhook.
Related
- HTTP Webhooks — the request/response contract including HTTP 429.
- Error codes — full catalog including
TOO_MANY_REQUESTSandRATE_LIMITED.