API reference
Drive automations from your own backend. Register users, track activity, record purchases, and let Driploop run your email flows — every call is logged and traceable.
Setup with AI
Copy one complete prompt for Cursor, Claude, Copilot, or Codex.
The prompt tells your coding assistant how to build a server-side Driploop client, keep the secret key out of client code, register users on sign-up, report login and activity events (with consistent event names for per-event flows) only for users already registered via POST /users (unregistered ids return 404 not_found), record purchases, update metadata, report product subscription changes, handle user deletion, and implement safe retries.
You will still create your own API key in Settings. The prompt tells the assistant to read it from DRIPLOOP_API_KEY, not to ask you to paste the secret into chat.
Base URL
https://driploop.net/api/v1API keys
dl_live_.Get started Authentication
Every request must be authenticated with one of your app's API keys.
Send your key in the Authorization header as a bearer token, or in the x-api-key header. Keys look like dl_live_xxx.
Authorization: Bearer dl_live_xxx
# — or —
x-api-key: dl_live_xxxCreate keys in Settings. The full key is shown once at creation time — store it securely. Missing, invalid, or revoked keys are rejected with 401 unauthorized.
Response envelope
Every response — success or error — is a JSON envelope carrying a correlationId.
Success
{
"data": { /* payload */ },
"correlationId": "cor_..."
}Error
{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"details": { /* optional */ }
},
"correlationId": "cor_..."
}The correlationId ties together every automation action a single request causes — the flows it triggers, the jobs it schedules, and the emails that go out. Use it to trace an outcome end-to-end in the Automation log.
Rate limits
Limits are enforced per API key.
Each API key may make up to 120 requests per minute. Every response includes the current window state:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1717900800X-RateLimit-Reset is a Unix timestamp (seconds). Once exceeded, requests return 429 rate_limited until the window rolls over.
Idempotency & de-duplication
Safe to retry — Driploop won't double-act on the same event.
Emails are de-duplicated per flow-execution + node
A given email node in a flow execution sends at most once (honoring its send-once / max-count settings). Re-delivering the same event won't double-send.
Register before activity, metadata, or purchases
Activity events and other user-scoped calls require an externalUserId registered via POST /users. Driploop never auto-creates users from POST /events/activity or similar calls — unregistered or inactive users get 404 not_found.
Re-registering a user updates instead of duplicating
Users are unique per (appId, externalUserId). Calling POST /usersagain with the same id updates the record. The response's data.created flag tells you which happened (201 create, 200 update).
Error codes
Errors use a stable string code alongside the HTTP status.
| HTTP | Code | When |
|---|---|---|
401 | unauthorized | Missing, invalid, or revoked API key. |
400 | bad_request | The request was malformed. |
404 | not_found | The referenced user or resource does not exist — e.g. activity/metadata/purchase sent for an externalUserId not yet registered via POST /users. |
422 | validation_error | Body failed schema validation; see error.details. |
429 | rate_limited | The per-key rate limit was exceeded. |
500 | internal_error | An unexpected server error occurred. |
Endpoints
All paths are relative to the base URL. Send the Authorization header with every request.
/api/v1/usersRegister a new tracked user or update an existing one.
user.registereduser.metadata_updatedBody
| Field | Type | Description |
|---|---|---|
externalUserIdreq | string | Your stable id for the user. |
emailopt | string | Used as the email recipient. |
metadataopt | object | Arbitrary traits — e.g. name, plan, email_verified. Use snake_case for multi-word keys. Strings, numbers, and booleans. |
Request
curl -X POST 'https://driploop.net/api/v1/users' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"externalUserId": "user_42",
"email": "[email protected]",
"metadata": {
"plan": "free",
"email_verified": false,
"name": "Ada"
}
}'Response
{
"data": {
"id": "...",
"externalUserId": "user_42",
"email": "[email protected]",
"status": "active",
"created": true
},
"correlationId": "cor_..."
}/api/v1/users/:externalUserIdDelete or deactivate a user per your app's delete policy. Any in-flight automation for the user is cancelled and no further emails are sent.
Request
curl -X DELETE 'https://driploop.net/api/v1/users/user_42' \
-H 'Authorization: Bearer dl_live_xxx'Response
{
"data": {
"externalUserId": "user_42",
"deleted": false,
"deactivated": true
},
"correlationId": "cor_..."
}/api/v1/events/activityRecord an activity event for a registered user. Updates the user's last-active timestamp. Returns 404 if the externalUserId has not been registered via POST /users.
user.first_activityuser.activeactivity.event:{name}Body
| Field | Type | Description |
|---|---|---|
externalUserIdreq | string | The user the activity belongs to. Must already be registered via POST /users. |
eventreq | string | Your event name — e.g. login, checkout_completed, feature_used. Must exactly match the Activity event name on any flow that should start. |
metadataopt | object | Extra context for the event. |
Request
curl -X POST 'https://driploop.net/api/v1/events/activity' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"externalUserId": "user_42",
"event": "login",
"metadata": { "source": "web" }
}'Response
// 201 — user registered via POST /users
{
"data": {
"id": "...",
"event": "login",
"firstActivity": true,
"recordedAt": "2026-06-09T12:00:00.000Z"
},
"correlationId": "cor_..."
}
// 404 — externalUserId not registered
{
"error": {
"code": "not_found",
"message": "User \"user_42\" is not registered. Register with POST /users first."
},
"correlationId": "cor_..."
}/api/v1/users/:externalUserId/metadataMerge keys into a user's metadata. Use this for product subscription state (e.g. subscribed, plan) and other traits.
user.metadata_updatedBody
| Field | Type | Description |
|---|---|---|
metadatareq | object | Keys to set or overwrite — e.g. subscribed, plan, seats. |
Request
curl -X PATCH 'https://driploop.net/api/v1/users/user_42/metadata' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{"metadata":{"subscribed":true,"plan":"pro"}}'Response
{
"data": {
"externalUserId": "user_42",
"metadata": { "subscribed": true, "plan": "pro" }
},
"correlationId": "cor_..."
}/api/v1/users/:externalUserId/purchaseRecord a purchase. Purchase dates power renewal, trial-ending, and win-back flows.
purchase.recordedBody
| Field | Type | Description |
|---|---|---|
purchaseDatereq | date | ISO or date string. |
purchaseEndDateopt | date | When the subscription/term ends. |
productIdopt | string | Your product identifier. |
amountopt | number | Non-negative amount. |
currencyopt | string | e.g. "USD". |
Request
curl -X POST 'https://driploop.net/api/v1/users/user_42/purchase' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"purchaseDate": "2026-06-09",
"purchaseEndDate": "2027-06-09",
"productId": "pro_annual",
"amount": 199,
"currency": "USD"
}'Response
{
"data": {
"id": "...",
"externalUserId": "user_42",
"purchaseDate": "2026-06-09T00:00:00.000Z",
"purchaseEndDate": "2027-06-09T00:00:00.000Z",
"productId": "pro_annual",
"amount": 199,
"currency": "USD"
},
"correlationId": "cor_..."
}Product subscriptions
Report plan and billing changes with two endpoints — state traits vs. billing events.
Subscription state → PATCH /users/:id/metadata
When a user subscribes, upgrades, downgrades, or cancels your product, patch traits such as subscribed, plan, or subscription_status. Each key is upserted — sending subscribed: false a week after subscribed: true leaves the final state as false. Fires user.metadata_updated.
Billing events → POST /users/:id/purchase
From your billing webhook (e.g. Stripe), record subscribe and renew events with purchaseDate, purchaseEndDate, and productId. Powers renewal-reminder, trial-ending, expiry, and win-back flows. Fires purchase.recorded.
Subscribe (state)
curl -X PATCH 'https://driploop.net/api/v1/users/user_42/metadata' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{"metadata":{"subscribed":true,"plan":"pro"}}'Cancel (state)
curl -X PATCH 'https://driploop.net/api/v1/users/user_42/metadata' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "metadata": { "subscribed": false, "plan": "free" } }'Typical wiring: on subscribe → POST /purchase + PATCH /metadata with subscribed: true; on cancel → PATCH /metadata with subscribed: false.
Triggers reference
Which API calls can start which flows. Pick the matching trigger type on your flow's trigger node, then activate the flow.
Per-event activity flows
For product-specific behavior (first login, checkout, feature used), use the Activity event trigger in the flow builder and enter the event name you will send in POST /events/activity. The names must match exactly — if your flow listens for checkout_completed, your API call must send "event": "checkout_completed". This is separate from user.active, which fires on every activity regardless of name.
| API call | Fires trigger |
|---|---|
| POST /users (new) | user.registered |
| POST /users (existing, with metadata) | user.metadata_updated |
| POST /events/activity (first ever) | user.first_activity |
| POST /events/activity | user.active |
| POST /events/activity (event name matches flow) | activity.event:{name} |
| PATCH /users/:id/metadata | user.metadata_updated |
| POST /users/:id/purchase | purchase.recorded |
Webhooks
Driploop sends outbound events, not inbound webhooks.
There is no inbound webhook endpoint — you push events with the API calls above. To call your systems during a flow, add a Webhook action node in the flow builder; its execution (and any failure) is recorded in the Automation log.
cURL quickstart
Register your first user. Replace the placeholder key with one of your own.
curl -X POST 'https://driploop.net/api/v1/users' \
-H 'Authorization: Bearer dl_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"externalUserId": "user_42",
"email": "[email protected]",
"metadata": {
"plan": "free",
"email_verified": false,
"name": "Ada"
}
}'