Overview
POST /api/drip is a per-request intake endpoint that accepts the exact same
body as POST /api/messages but is built for bulk,
bursty traffic — HubSpot and other CRMs fan enrollments out in parallel and
retry any 429/timeout for up to 3 days, which turns a rate limit into a retry
storm and duplicate sends.
/api/drip solves that:
- Never rate-limited. It is exempt from the 60/min send limiter, so a blast is
never rejected with
429(an infra backstop exists only for runaway abuse — see Limits). - Fast-acks
202in well under 2 seconds. - Drips safely. Every accepted message is inserted as a jittered scheduled send, then the existing worker + per-line ban protector pace the real sends so a line is never pushed past its safe cadence (which is what protects your iMessage lines from Apple).
To switch a HubSpot/CRM integration from
/api/messages to /api/drip, change
ONLY the URL you POST to. The request body, auth, and per-request contract are
identical. Use /api/drip for bulk enrollment blasts; keep /api/messages for
low-volume, send-now traffic where you want the immediate delivery status back.When to use which
Request
POST https://app.tuco.ai/api/drip
Auth: Authorization: Bearer <YOUR_API_KEY> (same key as /api/messages; a
Clerk session also works). Method: POST only.
Body: the same per-message shape as POST /api/messages.
The delivery/routing fields are forwarded verbatim: message, messageType,
recipientPhone / recipientEmail / recipientName, leadId, hsContactId /
hsPortalId, ghlContactId / ghlLocationId, pdCompanyId / pdPersonId /
pdLeadId, attachmentUrls / attachmentNames, sendFallbackSmsOnFailed /
smsFallbackEnabled / smsFallbackMessage / forceFallback, correlationId /
traceId, and the send-window settings timezone / sendWindowStart /
sendWindowEnd / allowedDaysOfWeek (honored — a message is clamped into your
window before it drips).
Campaign-orchestration fields are not applied by
/api/drip (it is a
per-message shock-absorber, not a campaign runner): batchId, stepIndex,
eligibleLineIds, stickinessPolicy, the failover knobs, isAgentDriven, and
needsAiPersonalization are ignored. Use campaigns for those.scheduledDate— always overwritten with a jittered future time (min now- 30s, spread over ~5 min) so the send is queued, never fired at intake. Any
scheduledDateyou pass is ignored.
- 30s, spread over ~5 min) so the send is queued, never fired at intake. Any
- Line selection — if you omit
fromLineId, a line is chosen by the same workspace round-robin/api/messagesuses (a persisted, workspace-wide rotation so no single line is hammered). An explicitfromLineIdis honored and validated to your workspace.
string
Optional. HubSpot retries will resend — pass a stable per-message
Idempotency-Key and a retry returns the original 202 instead of queueing a
second message. Even without it, an atomic 60-minute dedup guard on
(workspace, recipient, body) collapses accidental duplicates.Example
Response
202 Accepted — fast-ack. The request is buffered and the message is created a
moment later, then sent still later (paced per line). See below.
string
Always
"queued".string
Tracking id for this request (echoed from your
correlationId / X-Correlation-Id if you sent one). The message itself is created shortly after the ack, so no messageId is returned here — grep this id in your webhook payloads to correlate.string
ISO 8601 — the jittered future time the message becomes eligible to send. If you set a send window it is clamped into it; the ban protector may push it later still at drain time if the line is at its daily cap.
number
The jitter applied over the +30s floor, for observability.
Idempotency-Key) returns 200 with the original ack — no
second message. Same-content duplicates (workspace, recipient, body) within 60
minutes are also collapsed to one message downstream.
What happens next
The intake is buffered (Redis): it accepts your blast instantly and never touches the database on the request path, so it absorbs large parallel bursts. A paced worker then drains the buffer at a database-safe rate and creates each message as a scheduled send (source: "hubspot-drip"), visible in Unibox →
Scheduled. At each message’s scheduled time the per-line ban protector allows
it only if the line is under its safe cadence (150/day, ~20–50 new
conversations/day, ~3–7 new/hr) and otherwise reschedules it (hourly cap →
next hour, daily cap → next workspace morning). Two independent throttles —
buffer→database, then database→Apple — so a burst in never becomes a burst out.
Nothing is dropped. Delivery status flows through the same
message webhooks as /api/messages.
Limits
/api/drip is not subject to the 60/min send limit and will not 429 a normal
blast. The only limit is a high infra backstop on the buffered intake: ~1,000
requests/sec sustained per workspace (a 10,000-request burst bucket). It trips
solely on runaway/abuse; if you ever hit it you receive 429 + Retry-After and
can retry safely. Because the intake is buffered, database write pressure is
governed by the paced drainer, not by how fast you post.
See Errors & rate limits for the full table.