API reference

All routes below live under the JSON API unless noted. Use this page with the programmatic quickstart for copy-paste examples. Building an external LLM or automation agent? Read External agents guide (documentation URLs, sessions vs enrollments, what to call when).

Base URL: https://drippulse.io/api/v1 (or your self-hosted origin + /api/v1)
Health (unauthenticated): GET /health and GET /api/v1/health
On this page

Authentication

Most /api/v1/* endpoints require a Bearer token:

Authorization: Bearer <api_key_or_jwt>

Org-scoped resources only return data for the organization attached to the key or session.

JSON, errors, collections & idempotency

Format

Errors

Typical error bodies include error (string) or errors / message (see endpoint). Always read the response body for validation detail.

HTTPTypical cause
400Malformed JSON or missing required parameter.
401Missing or invalid Authorization bearer.
403Authenticated but not permitted for the resource.
404Unknown id for this org.
422 / 400Validation failed — often includes field messages for workflows or agents.
429Rate limited — backoff; honor Retry-After when set.
5xxTransient server error — retry with backoff for safe operations.

List / collection responses

Many index endpoints return a JSON object with a plural key (e.g. workflows: [...]) and total_count. Current workflow listing returns the full collection for the organization ordered by recency — plan pagination in your client if you expect very large graphs.

Idempotency (summary)

Agent endpoints accept idempotency_key in the JSON body for deduplication per agent. Workflow and webhook subscription creates are not automatically idempotent. Full matrix: Production: retries & idempotency.

Admin / session login

For trusted scripts only; never expose admin credentials in client-side code.

MethodPathNotes
POST/api/v1/admin/loginJSON body: username, password → JWT + admin payload
POST/api/v1/sessions/adminAlias of admin login
POST/api/v1/auth/loginAlias for programmatic clients
GET/api/v1/admin/verifyValidate token

Agents

MethodPathDescription
GET/agentsList agents
POST/agents/spawnDeploy from template or workflow
GET/agents/:idShow agent
PATCH/agents/:idUpdate (e.g. status, config)
DELETE/agents/:idDestroy agent
GET/agents/:id/executionsExecution history
GET/agents/:id/statusStatus snapshot
GET/agents/:id/logsLogs
GET/agents/:id/costsCost summary
GET/agents/:id/metricsAggregated metrics
POST/agents/:id/inferenceTemplate agent — structured JSON / LLM
POST/agents/:id/executeGeneric execute (policy-driven)
POST/agents/:id/score_leadLead scorer step

Agents — usage examples & errors

Purpose: Run org-scoped automation units (templates or workflow-bound) with observable executions.

Auth: Authorization: Bearer with org API key or session JWT.

POST /api/v1/agents/spawn — top-level fields or nested agent: { ... }. Required: name, type (template | workflow | code). Conditional ids:

FieldRequired whenNotes
namealwaysUnique per org (duplicate name rejected).
typealwaystemplate, workflow, or code.
template_idtype=templateMust reference an existing template.
workflow_idtype=workflowMust belong to current org.
code_idtype=codeCustom code agent reference.
configurationoptionalSanitized key-value map for runtime tuning.
{
  "name": "Inbound Lead Qualification",
  "type": "template",
  "template_id": 1,
  "configuration": { "min_score_threshold": 0.72 }
}

Response 201 (shape):

{
  "id": 42,
  "name": "Inbound Lead Qualification",
  "status": "active",
  "template_id": 1
}

POST /api/v1/agents/:id/execute — include idempotency_key in JSON for safe retries:

{
  "input": { "company": "Acme", "industry": "SaaS" },
  "idempotency_key": "partner-evt-20260402-xyz"
}

Common errors:

StatusMeaning
401Missing or invalid Bearer token.
403Authenticated but not allowed for this org or action.
404Agent or template not found for this org.
422Invalid body (validation); read JSON errors or message.
5xxTransient failure — retry with backoff; keep same idempotency_key for execute paths.

Operational notes: Execution rows appear in GET /agents/:id/executions; first logs may lag a few seconds. Side effects depend on agent policy (LLM calls, integrations).

Agent templates

MethodPathDescription
GET/agent_templatesList
POST/agent_templatesCreate
GET/agent_templates/:idShow
PATCH/agent_templates/:idUpdate
DELETE/agent_templates/:idDestroy
PATCH/agent_templates/:id/execution_policyUpdate execution policy

Webhooks (outbound catalog)

MethodPathDescription
GET/webhooksList configured webhooks
GET/webhooks/catalogAvailable event types / schema
POST/webhooksCreate
GET/webhooks/:idShow
PATCH/webhooks/:idUpdate
DELETE/webhooks/:idDestroy
POST/webhooks/:id/testSend test delivery
GET/webhooks/:id/deliveriesDelivery history

Outbound webhooks — create & deliveries

Purpose: Deliver org events to your HTTPS endpoint; each subscription has a secret for HMAC verification (see Production readiness).

Auth: Bearer required for management routes.

POST /api/v1/webhooks — required: url, event_type; optional: active (default true). Example:

{
  "url": "https://hooks.example.com/drippulse",
  "event_type": "workflow.failed",
  "active": true
}

Response 201 includes id, secret (store safely), url, event_type. Event catalog: GET /api/v1/webhooks/catalog.

GET /api/v1/webhooks/:id/deliveries — audit recent delivery attempts (status codes, timestamps).

Common errors: 400 missing url or event_type; 401/403 auth; 404 unknown id.

Standard resources :projects — index, show, create, update, destroy.

GET/POST /projects
GET/PATCH/DELETE /projects/:id

Team members

GET/POST /team_members
GET/PATCH/DELETE /team_members/:id

Workflows & enrollments

MethodPathDescription
GET/workflowsList
POST/workflowsCreate
GET/workflows/step_typesStep type catalog (self-describing builder)
GET/workflows/:idShow
PATCH/workflows/:idUpdate
DELETE/workflows/:idDestroy
POST/workflows/:id/testDry-run / test execution
GET/workflows/:workflow_id/enrollmentsList enrollments (ops summary per row)
GET/workflows/:workflow_id/enrollments/:idEnrollment detail: action timeline, deliveries, nurture events
POST/workflows/:workflow_id/enrollments/bulkBulk create enrollments
DELETE/workflows/:workflow_id/enrollments/:idRemove one enrollment (test cleanup / cancel run)

Enrollment list, step funnel & dashboard parity

Purpose: Operate and observe live per-contact runs of a saved workflow. External agents should treat this as the primary visibility API; it mirrors the workflow Enrollments tab in the dashboard.

GET /api/v1/workflows/:workflow_id/enrollments returns:

Each element of enrollments[] includes the core enrollment fields (id, workflow_id, agent_id, lead_ref, current_step_index, status, next_run_at, timezone, timestamps) plus summary helpers:

step_funnel shape (illustrative):

{
  "step_funnel": {
    "steps": [
      {
        "step_index": 0,
        "step_number": 1,
        "name": "Start",
        "type": "trigger",
        "active_leads": 12,
        "waiting_leads": 0,
        "running_leads": 12
      },
      {
        "step_index": 1,
        "step_number": 2,
        "name": "Wait 30m",
        "type": "wait",
        "active_leads": 4,
        "waiting_leads": 3,
        "running_leads": 1
      }
    ],
    "totals": {
      "active": 14,
      "completed": 40,
      "failed": 1,
      "cancelled": 0,
      "total_enrollments": 55
    }
  }
}

For each step bucket, active_leads counts enrollments currently positioned on that step; waiting_leads is the subset whose next_run_at is in the future (paused for a wait or schedule); running_leads is active_leads - waiting_leads.

GET /api/v1/workflows/:workflow_id/enrollments/:id returns a single object with:

Dashboard: Open a saved workflow → Enrollments tab. You can add enrollments via Add enrollments (single contact with optional JSON fields, bulk JSON array, or selecting CRM contacts / CRM leads), set timezone, and optionally bind a workflow agent — equivalent to POST .../enrollments/bulk including optional agent_id.

For how external agents should load documentation (skills URL), what “sessions” means on the customer boundary, and decision trees, see External agents guide.

Workflows — create, test, errors

Purpose: Define multi-step automation (trigger → logic → actions) scoped to your org and optional project.

Auth: Bearer token. Write access typically Admin/Editor per product rules.

POST /api/v1/workflows — send top-level fields or wrap in workflow: { ... }. Required: name. Common fields:

FieldTypeNotes
namestringRequired.
descriptionstringOptional.
statusstringe.g. draft, active — product-specific enum.
stepsarrayEach step: type, name, and step parameters. Canonical shape: all parameters under config (matches the dashboard). Top-level fields beside type/name/id/position are merged at runtime but are normalized into config on save.
integrationsarrayIntegration references as stored on the model.
versionintegerMay be managed server-side when steps change.
{
  "name": "Inbound Lead Qualification",
  "description": "Draft from API",
  "status": "draft",
  "steps": [
    { "type": "trigger", "name": "Start", "config": { "frequency": "hourly" } },
    { "type": "log", "name": "Done", "config": { "message": "ok", "level": "info" } }
  ],
  "integrations": []
}

Step shape (email / wait): Put subject, body, to, integration_id, etc. under config — same as GET /workflows/step_types field names. Example wait step: { "type": "wait", "name": "Pause", "config": { "duration": 1, "unit": "hours" } }. Sending those keys at the step top level still works for execution/tests, but the API persists them inside config so the visual editor and GET responses stay aligned.

Response 201 (shape): includes id, persisted steps, status. Creation is synchronous; first production runs may appear shortly after scheduling or triggers fire.

POST /api/v1/workflows/:id/test — dry run. Optional body keys: sample_data, input, or runtime_payload (same merge; pick one). Response includes test_fixture_profile and final sample_data. Email, outbound webhooks, and CRM steps use [test] logs / stubs — not production sends (see Email: test stubs vs live ESP).

POST …/workflows/:id/test — fixture merge semantics (avoid surprises)

API clients often assume an empty or partial sample_data means “only my keys exist.” In practice the server always starts from a fixture profile, then merges your object.

  1. Fixture profile — Default is minimal: keys such as first_name, last_name, email, score. If you send "fixture_profile": "demo" (or full / rich / legacy), the server adds many extra keys (company_name, industry, segment-style fields, …). The dashboard Test button uses fixture_profile: demo — match it in API tests if you want identical data to the UI.
  2. Shallow merge of your payload — Your sample_data / input / runtime_payload is shallow-merged on top of that fixture: for the same top-level key, your value wins. Keys you omit remain from the fixture — so you still see company_name in the merged bag when using demo unless you override or clear it.
  3. Per-step outputs — After each step, the runtime bag is updated by another shallow merge of step outputs. Nested objects are not deep-merged: a step that sets payload replaces a previous payload object entirely.

Tip: To validate templates with a truly minimal bag, send explicit empty strings or a slim fixture_profile and override every key your template references; or rely on test_fixture_profile in the response to confirm which profile ran.

Email and integrations — /test / agent runs vs live enrollments

SurfaceEmail / ESP behavior
POST …/workflows/:id/testStubbed. Logs show [test] / “would send” style messages. No live ESP delivery; connectors are not required for most stubs.
Workflow-type POST …/agents/:id/executeFollows the agent’s execution path. Treat as production-capable unless your agent or workflow is explicitly test-only: if the run resolves real integration_connections and live modes, email can send for real.
POST …/enrollments/bulk (live enrollment)Live path. Steps use step config.integration_id to pick the adapter (e.g. SendGrid). For providers that use org integration connections, the runtime resolves a row in /api/v1/integration_connections (by provider + optional connection_key, default default). Missing or unusable connections can cause skips or errors depending on step type.

Rule of thumb: /test = safe preview; enrollments bulk = real per-contact execution subject to connections and step config.

POST /api/v1/workflows/:workflow_id/enrollments/bulk — starts one enrollment per contacts[] object (mode: live; real side effects per integration_id and connections). Body: { "contacts": [ { "email": "a@b.com", "first_name": "...", "lead_ref": "crm-123" } ], "timezone": "UTC", "agent_id": 9 }. Optional wrapper: { "enrollment": { "contacts": [ ... ] } }. Link CRM rows: set lead_ref or pass id / uuid / public_id / crm_id / lead_id (first present becomes lead_ref). Require Content-Type: application/json. Responses: 201 with created_count, enrollment_ids, and optional skipped_invalid_rows; 422 when nothing is created (see edge cases). Idempotency: each call creates new rows; dedupe before call or use DELETE .../enrollments/:id for mistakes. Workflow execute agents support idempotency_key instead.

Enrollment bulk — 422 and other error shapes

When no enrollment rows are created, the API returns 422 with a JSON body (not 201 with zero creates). Typical cases:

error (when present)Meaning
workflow_archivedWorkflow status is archived; enrollments are rejected.
contacts_requiredBody missing a top-level contacts array (or not wrapped correctly in enrollment.contacts).
contacts_emptycontacts was [].
no_enrollments_createdEvery row was invalid (e.g. non-object entries), wrong Content-Type, or multipart/form instead of JSON objects.

404 on bulk is used when agent_id is sent but no agent exists in the org. Successful 201 may include skipped_invalid_rows when some array elements were skipped but at least one enrollment was created.

DELETE /api/v1/workflows/:workflow_id/enrollments/:id — deletes that enrollment (stops future ticks for that row). 204 on success.

Enrollment list and detail JSON (including step_funnel on the index) and dashboard parity are documented under Enrollment list, step funnel & dashboard parity above.

CRM → workflow: Create leads with POST /api/v1/crm/leads, then enroll the same person with enrollments/bulk using the returned id as lead_ref or top-level id plus full contact fields for email content.

Webhook step types: The builder "Webhook" input step stores inbound contract metadata only (no hosted ingress URL in-app today). Use Outbound Webhook (webhook_callback) for HTTP out. See workflow guide — webhooks.

Event bridge (recommended for inbound events)

There is no tenant-scoped "POST here to start workflow" URL for arbitrary JSON. The canonical integration pattern is: your server receives the webhook or form POST, then calls enrollments/bulk or agents/execute. Full walkthrough, sample code, and idempotency notes: Event bridge guide.

Pagination: GET /workflows returns a collection for the org; if your client adds page / per_page follow response metadata when present.

Idempotency (workflows): No global header on workflow or enrollment bulk creates — dedupe in your orchestration layer. Prefer idempotency_key on POST /agents/:id/execute when using workflow agents.

Common errors:

StatusMeaning
400Invalid step graph or parameters.
401Missing/invalid API key or session.
403Insufficient role for workflow write.
404Workflow id not in org.
422Enrollment bulk and other validation failures — see Enrollment bulk — 422 edge cases.

API keys

MethodPathDescription
GET/api_keysList (secrets redacted)
POST/api_keysCreate — full key returned once
GET/api_keys/:idShow
DELETE/api_keys/:id/revokeRevoke
GET/api_keys/usageUsage stats

API keys — create & revoke

Purpose: Server-side access to /api/v1 on behalf of your organization.

POST /api/v1/api_keys — optional name, optional expires_in (days). The full key string is returned only on create.

{
  "name": "Production automation",
  "expires_in": 365
}

Response 201 (illustrative):

{
  "id": 9,
  "name": "Production automation",
  "key": "adcrm_live_…",
  "key_prefix": "adcrm_live_",
  "last_four": "abcd",
  "expires_at": null,
  "created_at": "2026-04-02T12:00:00Z",
  "message": "Save this API key now! You won't be able to see it again."
}

GET /api/v1/api_keys — listed keys omit full secret (redacted).

DELETE /api/v1/api_keys/:id/revoke — invalidates the key; clients should rotate before revoking the last working credential.

Common errors: 401 if calling without session/key to create; 422 validation on name or limits.

Reports

MethodPathDescription
GET/reportsList
POST/reports/generateGenerate report
GET/reports/:idShow
DELETE/reports/:idDestroy
GET/reports/:id/downloadDownload artifact
POST/reports/:id/scheduleSchedule

Account & billing (account)

MethodPathDescription
GET/accountOrganization / profile
PATCH/accountUpdate account
GET/account/billingBilling summary
PATCH/account/billingUpdate billing fields

Analytics

GET /analytics/metrics
GET /analytics/charts

Monitoring

GET /monitoring/agents
GET /monitoring/reasoning/:agent_id

Reports & monitoring — quick usage

Reports: POST /api/v1/reports/generate accepts a report type and parameters in JSON; GET /api/v1/reports/:id polls completion; GET .../download fetches the artifact when ready.

Monitoring: GET /api/v1/monitoring/agents returns fleet signals; GET /api/v1/monitoring/reasoning/:agent_id surfaces reasoning-oriented diagnostics when enabled.

Common errors: 404 missing report/agent; 429 if heavy generation is throttled—retry with backoff.

Operational playbooks: Monitoring & troubleshooting.

Integration connections

Slack, HubSpot, Salesforce, SendGrid, Google Workspace, Microsoft 365, Stripe, Zoom, Zendesk, Gong, etc.

GET/POST /integration_connections
GET/PATCH/DELETE /integration_connections/:id

Integrations — usage notes

Purpose: Store OAuth or API credentials for downstream systems your workflows and agents call.

Auth: Bearer required. Responses include connection type and status; secrets are never returned in full after creation.

Side effects: Creating or refreshing a connection may trigger a test call to the provider; failures surface as 4xx with an explanatory message.

Common errors: 400/422 invalid tokens; 409 duplicate connection type when only one is allowed per org.

Workflow steps: integration_id vs connections — Step config.integration_id picks the adapter (e.g. sendgrid, hubspot, slack). For providers that use ConnectorDispatcher, the runtime resolves a row in /integration_connections by provider + optional connection_key (defaults to default). If no usable connection exists, live CRM/email may fail or audit-skip depending on the step. Test mode (POST .../test) does not require connections: email shows [test] Would send…; SendGrid is still skipped without a connection; tracked nurture sends need a live enrollment and app mailer configuration.

integration_id (examples)Live behavior (summary)
sendgrid / sgUses SendGrid integration_connection; sends real mail when configured.
nurture, internal, open-tracking pathsTracked / nurture mailer path (enrollment context required for opens pixel).
hubspot / hs, salesforce / sfCRM steps call connector when a matching connection exists; [test] stubs in dry run.
slackAlert steps post when a Slack connection exists.

External tool adapters (direct integration layer)

Supported provider keys: google_workspace, microsoft365, slack, salesforce, hubspot, stripe, sendgrid, zoom, zendesk, gong.

Aliases: gmail -> google_workspace, outlook/office365 -> microsoft365, sf/sfdc -> salesforce, hs -> hubspot, sg -> sendgrid.

Workflow invocation: Use step type integration_action with config.integration_id, config.action, config.payload, and optional config.connection_key.

{
  "type": "integration_action",
  "name": "Create support ticket",
  "config": {
    "integration_id": "zendesk",
    "action": "create_ticket",
    "connection_key": "default",
    "payload": {
      "subject": "Help needed for {{email}}",
      "description": "Workflow generated issue summary",
      "requester_email": "{{email}}"
    }
  }
}

Guardrails: keep secrets only in /integration_connections; pass references in workflow config; runtime resolves credentials server-side; responses hide secret values after creation.

Full integration guide with action map: External tool adapters.

Data sources & runs

MethodPathDescription
GET/data_sourcesList
POST/data_sourcesCreate
GET/data_sources/:idShow
PATCH/data_sources/:idUpdate
DELETE/data_sources/:idDestroy
POST/data_sources/:id/previewPreview import
POST/data_source_runsStart run
GET/data_source_runs/:idRun status

CRM starter layer

Org-scoped CRM under /api/v1/crm/…. Full import/export and pipeline concepts: CRM data portability, Opportunity pipelines & Kanban.

Export & import; change events

MethodPathDescription
GET/crm/exportJSON bundle (accounts, contacts, opportunities with pipeline_id / pipeline_stage_id, leads, activities)
POST/crm/importUpsert same shape
GET/crm/change_eventsAudit stream (optional filters)

Opportunity pipelines

MethodPathDescription
GET/crm/opportunity_pipelinesList pipelines + nested stages; default_pipeline_id
POST/crm/opportunity_pipelinesClone org default to user pipeline ({ "pipeline": { "name" } })
PATCH/crm/opportunity_pipelines/:idUpdate user-owned pipeline (name, stages)
DELETE/crm/opportunity_pipelines/:idDelete if empty
POST/crm/opportunity_pipelines/:id/set_defaultSet user default pipeline

Accounts, contacts, opportunities, leads, activities

Standard REST collections; each supports CSV import on POST …/import_csv where listed in routes.

MethodPathNotes
GET/crm/accounts/crm/accounts/:idCRUD + destroy (soft delete)
GET/crm/contactsCRUD
GET/crm/opportunitiesQuery: pipeline_id, q, filters; opportunity objects include name, pipeline_id, pipeline_stage_id, stage_position
POST/crm/opportunities/reorderBody: pipeline_stage_id, ordered_ids (public_id or UUID)
GET/crm/leadsCRUD
GET/crm/activitiesCRUD

Billing (Stripe)

MethodPathDescription
GET/billingSubscription state
POST/billing/subscribeCreate subscription
POST/billing/cancelCancel
POST/billing/reactivateReactivate
GET/billing/payment-methodsList payment methods
POST/billing/checkoutStripe Checkout session

Inbound webhooks & tracking (no Bearer auth)

These use provider signatures, shared secrets, or are public tracking endpoints—not your org API key.

MethodPathNotes
POST/webhooks/stripeStripe webhook
POST/webhooks/lead_scoringLead scorer inbound (signed)
POST/webhooks/nurture/bounceESP bounce / complaint
GET/track/n/pixel.gifOpen pixel
GET/track/n/clickClick redirect
GET / POST/track/n/unsubscribeList-unsubscribe

OAuth (browser)

Google sign-in starts outside JSON API:

GET /auth/google_oauth2
GET /auth/google_oauth2/callback

Try it

  1. Read the quickstart.
  2. Download the agent skills pack (includes an OpenAPI starter spec).
  3. Sign in and create an API key.
  4. Call GET /api/v1/agents with Authorization: Bearer …

DripPulse · drippulse.io · Documentation home