openapi: 3.1.0
info:
title: Drip BillableMetrics Workflows API
description: "\n# Drip API\n\n**Usage-based billing + execution ledger.**\n\n---\n\n## 60-Second Quickstart (Core SDK)\n\n### 1. Install\n\n```bash\nnpm install @drip-sdk/node\n```\n\n### 2. Set your API key\n\n```bash\n# Secret key - full API access (server-side only, never expose publicly)\nexport DRIP_API_KEY=sk_test_...\n\n# Use a secret key for the customer, billing, runs, pricing-plan, and webhook\n# flows documented in this reference.\n```\n\nOr use a \".env\" file (recommended):\n\n```bash\nnpm install dotenv\n# .env\nDRIP_API_KEY=sk_test_...\n```\n\nLoad your \".env\" at the top of your entry file:\n\n```typescript\nimport 'dotenv/config';\n```\n\n### 3. Create a customer and track usage\n\n```typescript\nimport 'dotenv/config';\nimport { drip } from '@drip-sdk/node';\n\n// Create a customer first\nconst customer = await drip.createCustomer({ externalCustomerId: 'user_123' });\n\n// Internal tracking only (no billing)\nawait drip.trackUsage({ customerId: customer.id, meter: 'api_calls', quantity: 1 });\n```\n\nThe `drip` singleton reads `DRIP_API_KEY` from your environment automatically.\n\n### Alternative: Explicit Configuration\n\n```typescript\nimport 'dotenv/config';\nimport { Drip } from '@drip-sdk/node';\n\n// Auto-reads DRIP_API_KEY from environment\nconst client = new Drip();\n\n// Or pass config explicitly with an Admin/Operator secret key\nconst clientWithSecret = new Drip({ apiKey: 'sk_test_...' });\n```\n\n### Full Example (Node.js)\n\n```typescript\nimport 'dotenv/config';\nimport { drip } from '@drip-sdk/node';\n\nasync function main() {\n // Verify connectivity\n await drip.ping();\n\n // Create a customer (at least one of externalCustomerId or onchainAddress required)\n const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });\n\n // Internal tracking (no billing)\n await drip.trackUsage({\n customerId: customer.id,\n meter: 'llm_tokens',\n quantity: 842,\n metadata: { model: 'gpt-4o-mini' },\n });\n\n // Billable usage\n await drip.charge({\n customerId: customer.id,\n meter: 'api_calls',\n quantity: 1,\n });\n\n // Record an execution lifecycle\n await drip.recordRun({\n customerId: customer.id,\n workflow: 'research-agent',\n events: [\n { eventType: 'llm.call', quantity: 1700, units: 'tokens' },\n { eventType: 'tool.call', quantity: 1 },\n ],\n status: 'COMPLETED',\n });\n\n console.log(`Customer ${customer.id}: usage + run recorded`);\n}\n\nmain();\n```\n\n```python\nfrom drip import drip\n\n# Create a customer first\ncustomer = drip.create_customer(external_customer_id=\"user_123\")\n\n# Internal tracking only (no billing)\ndrip.track_usage(customer_id=customer.id, meter=\"api_calls\", quantity=1)\n```\n\nThe `drip` singleton reads `DRIP_API_KEY` from your environment automatically.\n\n### Alternative: Explicit Configuration (Python)\n\n```python\nfrom drip import Drip\n\n# Auto-reads DRIP_API_KEY from environment\nclient = Drip()\n\n# Or pass config explicitly with an Admin/Operator secret key\nclient_with_secret = Drip(api_key=\"sk_test_...\")\n```\n\n### Full Example (Python)\n\n```python\nfrom drip import drip\n\n# Verify connectivity\ndrip.ping()\n\n# Create a customer (at least one of external_customer_id or onchain_address required)\ncustomer = drip.create_customer(external_customer_id=\"user_123\")\n\n# Internal tracking (no billing)\ndrip.track_usage(\n customer_id=customer.id,\n meter=\"llm_tokens\",\n quantity=842,\n metadata={\"model\": \"gpt-4o-mini\"}\n)\n\n# Billable usage\ndrip.charge(\n customer_id=customer.id,\n meter=\"api_calls\",\n quantity=1\n)\n\n# Record execution lifecycle\ndrip.record_run(\n customer_id=customer.id,\n workflow=\"research-agent\",\n events=[\n {\"event_type\": \"llm.call\", \"quantity\": 1700, \"units\": \"tokens\"},\n {\"event_type\": \"tool.call\", \"quantity\": 1},\n ],\n status=\"COMPLETED\"\n)\n\nprint(f\"Customer {customer.id}: usage + run recorded\")\n```\n\n**Expected result:**\n- No errors\n- Events appear in your Drip dashboard within seconds\n\n**Install:** `npm install @drip-sdk/node` or `pip install drip-sdk`\n\n**Set API key:** `export DRIP_API_KEY=sk_test_...` or use a \".env\" file with `DRIP_API_KEY=sk_test_...` and load it with `import 'dotenv/config'` (for Node.js)\n\n---\n\n## REST API Quick Start\n\n### Step 1: Create a customer\n\n```bash\ncurl -X POST https://api.drippay.dev/v1/customers \\\n -H \"Authorization: Bearer sk_test_YOUR_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"externalCustomerId\": \"user_123\"}'\n```\n\n**You'll get back:**\n```json\n{\n \"id\": \"cmlxxxxxx...\",\n \"externalCustomerId\": \"user_123\",\n \"status\": \"ACTIVE\"\n}\n```\n\n### Step 2: Create pricing\n\nUse the same meter string in your pricing plan and your usage calls:\n\n```bash\ncurl -X POST https://api.drippay.dev/v1/pricing-plans \\\n -H \"Authorization: Bearer sk_test_YOUR_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"API Calls\",\n \"unitType\": \"api_calls\",\n \"unitPriceUsd\": 0.001\n }'\n```\n\n### Step 3: Charge usage\n\n```bash\ncurl -X POST https://api.drippay.dev/v1/usage \\\n -H \"Authorization: Bearer sk_test_YOUR_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"customerId\": \"CUSTOMER_ID_FROM_STEP_1\",\n \"usageType\": \"api_calls\",\n \"quantity\": 100,\n \"idempotencyKey\": \"charge_001\"\n }'\n```\n\n---\n\n## SDK Methods\n\n### Node.js (`@drip-sdk/node`)\n\n| Method | Description |\n|--------|-------------|\n| `createCustomer()` | Create a customer |\n| `getCustomer()` | Get customer details |\n| `listCustomers()` | List all customers |\n| `trackUsage()` | Record internal usage without billing |\n| `charge()` | Record usage and charge (requires pricing plan) |\n| `emitEvent()` | Record an execution event |\n| `recordRun()` | Record a complete agent run |\n| `startRun()` | Start a run |\n| `endRun()` | End a run |\n| `getBalance()` | Get customer balance |\n\n### Python (`drip-sdk`)\n\n| Method | Description |\n|--------|-------------|\n| `create_customer()` | Create a customer |\n| `get_customer()` | Get customer details |\n| `list_customers()` | List all customers |\n| `track_usage()` | Record internal usage without billing |\n| `charge()` | Record usage and charge (requires pricing plan) |\n| `emit_event()` | Record an execution event |\n| `record_run()` | Record a complete agent run |\n| `start_run()` | Start a run |\n| `end_run()` | End a run |\n| `get_balance()` | Get customer balance |\n\n---\n\n## Two Modes\n\n| Mode | How | When |\n|------|-----|------|\n| **Tracking only** | `trackUsage()` / `track_usage()` | Pilots, analytics, no billing yet |\n| **Billing** | `charge()` + pricing plan | Ready to charge customers |\n\nBoth modes record usage in the ledger. The difference is whether a charge is created.\n\nUse `POST /v1/usage/internal` when you want tracking only.\n\n---\n\n## Authentication\n\n```\nAuthorization: Bearer sk_test_YOUR_KEY\n```\n\n### Key Types\n\n| Key Prefix | Use For |\n|------------|---------|\n| `sk_test_*` / `sk_live_*` | Server-side API access (secret key) |\n| `pk_test_*` / `pk_live_*` | Public key identifier. The role-protected endpoints in this reference require a secret key |\n\n### API Key Roles\n\nSecret keys (`sk_*`) are assigned a role that controls which endpoints they can access. Roles are hierarchical: higher roles include all permissions of lower roles.\n\n| Role | Level | Permissions |\n|------|-------|-------------|\n| `READONLY` | Lowest | List and read resources (customers, charges, pricing plans, events) |\n| `OPERATOR` | Mid | + Create customers, charge usage, track internal usage, emit events, manage runs |\n| `ADMIN` | Highest | + Create/update/delete pricing plans, manage contracts, manage API keys |\n\nPublic keys (`pk_*`) are always `READONLY`: they cannot create or modify resources.\n\n> **Important:** To create, update, or delete **pricing plans** and **contracts**, you must use a secret key (`sk_*`) with the **ADMIN** role. Using a lower-role key returns `403 Forbidden`.\n\n---\n\n## Idempotency\n\nAlways include `idempotencyKey` in POST requests to prevent duplicates:\n\n```json\n{\n \"customerId\": \"cmlxxxxxx...\",\n \"usageType\": \"api_calls\",\n \"quantity\": 1,\n \"idempotencyKey\": \"req_unique_12345\"\n}\n```\n\nSame key = same result. Safe to retry on network failures.\n\n---\n\n## Error Codes\n\n| Status | Meaning | Fix |\n|--------|---------|-----|\n| **400** | Bad request | Check request body matches schema |\n| **401** | Invalid API key | Verify `Authorization: Bearer sk_...` header |\n| **404** | Not found | Customer/pricing plan doesn't exist. Create customer first. |\n| **409** | Duplicate | Resource with this ID already exists |\n| **422** | Validation error | Check required fields and types |\n\n---\n\n## Important Notes\n\n- **Always create a customer first** for billable onboarding flows. You cannot use made-up customer IDs.\n- **`POST /v1/events`** records execution events (what happened). It does **not** auto-create charges.\n- **`POST /v1/usage`** records usage and creates charges if a matching pricing plan exists.\n- **`POST /v1/usage/internal`** records usage without billing.\n- **Numbers as strings**: Monetary amounts are returned as strings (e.g., `\"0.001000\"`) to preserve precision.\n"
version: 1.0.0
contact:
name: Drip Support
email: support@drippay.dev
x-logo:
url: https://drippay.dev/logo.svg
altText: Drip
servers:
- url: https://api.drippay.dev
description: Production
- url: http://localhost:3001
description: Development
security:
- bearerAuth: []
tags:
- name: Workflows
description: Define workflow templates for agent execution.
paths:
/v1/workflows:
post:
operationId: createWorkflow
summary: Create a workflow
tags:
- Workflows
description: Create a new workflow definition for tracking agent runs.
requestBody:
required: true
content:
application/json:
schema:
type: object
description: Request body for creating a new workflow.
properties:
name:
type: string
minLength: 1
maxLength: 255
description: Human-readable name for the workflow (required)
slug:
type: string
minLength: 1
maxLength: 100
pattern: ^[a-z0-9_-]+$
description: URL-safe identifier - must be lowercase alphanumeric with underscores or hyphens (required)
productSurface:
type: string
enum:
- API
- RPC
- WEBHOOK
- AGENT
- PIPELINE
- CUSTOM
description: Product category for dashboard grouping. Use API for REST APIs, RPC for blockchain calls, WEBHOOK for webhooks, AGENT for AI agents, PIPELINE for data pipelines, CUSTOM for other use cases.
default: CUSTOM
chain:
type: string
enum:
- ETHEREUM
- SOLANA
- POLYGON
- ARBITRUM
- OPTIMISM
- BASE
- AVALANCHE
- BSC
description: Blockchain network (optional, for RPC workflows only)
description:
type: string
maxLength: 1000
description: Optional description of what this workflow does
metadata:
type: object
additionalProperties: true
description: Optional metadata for custom tracking
required:
- name
- slug
description: Request body for creating a new workflow.
responses:
'201':
description: Workflow created
content:
application/json:
schema:
description: Workflow created
type: object
properties:
id:
type: string
description: Unique identifier for the workflow
name:
type: string
description: Human-readable name for the workflow
slug:
type: string
description: URL-safe identifier (lowercase, alphanumeric, hyphens, underscores)
productSurface:
type: string
enum:
- API
- RPC
- WEBHOOK
- AGENT
- PIPELINE
- CUSTOM
description: Product category for dashboard grouping. API = REST API calls, RPC = Blockchain RPC calls, WEBHOOK = Webhook deliveries, AGENT = AI agent executions, PIPELINE = Data pipelines, CUSTOM = Custom workflows
chain:
type: string
enum:
- ETHEREUM
- SOLANA
- POLYGON
- ARBITRUM
- OPTIMISM
- BASE
- AVALANCHE
- BSC
nullable: true
description: Blockchain network for RPC workflows (optional)
description:
type: string
nullable: true
description: Optional description of what this workflow does
metadata:
type: object
additionalProperties: true
nullable: true
description: Optional metadata for custom tracking
isActive:
type: boolean
description: Whether this workflow is currently active
createdAt:
type: string
format: date-time
description: When the workflow was created
updatedAt:
type: string
format: date-time
description: When the workflow was last updated
required:
- id
- name
- slug
- productSurface
- isActive
'400':
description: Validation error
content:
application/json:
schema:
description: Validation error
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
'401':
description: Unauthorized
content:
application/json:
schema:
description: Unauthorized
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
'409':
description: Workflow with this slug already exists
content:
application/json:
schema:
description: Workflow with this slug already exists
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
get:
operationId: listWorkflows
summary: List workflows
tags:
- Workflows
description: List all workflows for your business.
parameters:
- schema:
type: string
enum:
- API
- RPC
- WEBHOOK
- AGENT
- PIPELINE
- CUSTOM
in: query
name: productSurface
required: false
- schema:
type: boolean
in: query
name: isActive
required: false
- schema:
type: integer
minimum: 1
maximum: 100
default: 50
in: query
name: limit
required: false
responses:
'200':
description: List of workflows
content:
application/json:
schema:
description: List of workflows
type: object
properties:
data:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
slug:
type: string
productSurface:
type: string
description:
type: string
nullable: true
isActive:
type: boolean
runCount:
type: integer
createdAt:
type: string
format: date-time
count:
type: integer
'401':
description: Unauthorized
content:
application/json:
schema:
description: Unauthorized
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
/v1/workflows/{id}:
get:
operationId: getWorkflow
summary: Get workflow
tags:
- Workflows
description: Get a workflow by ID with usage statistics.
parameters:
- schema:
type: string
in: path
name: id
required: true
responses:
'200':
description: Workflow details
content:
application/json:
schema:
description: Workflow details
type: object
properties:
id:
type: string
name:
type: string
slug:
type: string
productSurface:
type: string
description:
type: string
nullable: true
metadata:
type: object
nullable: true
isActive:
type: boolean
stats:
type: object
properties:
totalRuns:
type: integer
completedRuns:
type: integer
failedRuns:
type: integer
totalEvents:
type: integer
totalCostUnits:
type: string
createdAt:
type: string
format: date-time
'401':
description: Unauthorized
content:
application/json:
schema:
description: Unauthorized
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
'404':
description: Workflow not found
content:
application/json:
schema:
description: Workflow not found
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
patch:
operationId: updateWorkflow
summary: Update workflow
tags:
- Workflows
parameters:
- schema:
type: string
in: path
name: id
required: true
responses:
'200':
description: Workflow updated
content:
application/json:
schema:
description: Workflow updated
'401':
description: Unauthorized
content:
application/json:
schema:
description: Unauthorized
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
'404':
description: Workflow not found
content:
application/json:
schema:
description: Workflow not found
type: object
properties:
error:
type: string
description: Error message
code:
type: string
description: Error code
details:
type: array
items:
type: object
properties:
path:
type: string
message:
type: string
description: Validation error details
required:
- error
- code
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: API Key
description: 'API key from Drip Dashboard. Format: `sk_live_...` or `sk_test_...`'