Skip to main content

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 202 in 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.
Two fields are managed for you and cannot be set:
  • 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 scheduledDate you pass is ignored.
  • Line selection — if you omit fromLineId, a line is chosen by the same workspace round-robin /api/messages uses (a persisted, workspace-wide rotation so no single line is hammered). An explicit fromLineId is 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.
A duplicate (same 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.