API v1

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/v1

API keys

Create an app in the dashboard, then generate a secret key in Settings. Secret keys start with 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_xxx

Create 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: 1717900800

X-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.

HTTPCodeWhen
401unauthorizedMissing, invalid, or revoked API key.
400bad_requestThe request was malformed.
404not_foundThe referenced user or resource does not exist — e.g. activity/metadata/purchase sent for an externalUserId not yet registered via POST /users.
422validation_errorBody failed schema validation; see error.details.
429rate_limitedThe per-key rate limit was exceeded.
500internal_errorAn unexpected server error occurred.

Endpoints

All paths are relative to the base URL. Send the Authorization header with every request.

POST/api/v1/users

Register a new tracked user or update an existing one.

Can trigger:user.registereduser.metadata_updated

Body

FieldTypeDescription
externalUserIdreqstringYour stable id for the user.
emailoptstringUsed as the email recipient.
metadataoptobjectArbitrary 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_..."
}
DELETE/api/v1/users/:externalUserId

Delete 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_..."
}
POST/api/v1/events/activity

Record 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.

Can trigger:user.first_activityuser.activeactivity.event:{name}

Body

FieldTypeDescription
externalUserIdreqstringThe user the activity belongs to. Must already be registered via POST /users.
eventreqstringYour event name — e.g. login, checkout_completed, feature_used. Must exactly match the Activity event name on any flow that should start.
metadataoptobjectExtra 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_..."
}
PATCH/api/v1/users/:externalUserId/metadata

Merge keys into a user's metadata. Use this for product subscription state (e.g. subscribed, plan) and other traits.

Can trigger:user.metadata_updated

Body

FieldTypeDescription
metadatareqobjectKeys 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_..."
}
POST/api/v1/users/:externalUserId/purchase

Record a purchase. Purchase dates power renewal, trial-ending, and win-back flows.

Can trigger:purchase.recorded

Body

FieldTypeDescription
purchaseDatereqdateISO or date string.
purchaseEndDateoptdateWhen the subscription/term ends.
productIdoptstringYour product identifier.
amountoptnumberNon-negative amount.
currencyoptstringe.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 callFires trigger
POST /users (new)user.registered
POST /users (existing, with metadata)user.metadata_updated
POST /events/activity (first ever)user.first_activity
POST /events/activityuser.active
POST /events/activity (event name matches flow)activity.event:{name}
PATCH /users/:id/metadatauser.metadata_updated
POST /users/:id/purchasepurchase.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"
    }
}'