Install
openclaw skills install drip-billingTrack AI agent usage and costs with Drip metered billing. Use when you need to record aggregate LLM usage, tool calls, agent runs, or other metered usage for billing.
openclaw skills install drip-billingTrack usage and costs for AI agents, LLM calls, tool invocations, and any metered workload.
Key scoping (least privilege):
pk_ (public) keys for usage tracking, customer management, and billing. This is sufficient for all skill operations.sk_ (secret) keys if you need admin operations: webhook management, API key rotation, or feature flags.pk_) cannot manage webhooks, rotate API keys, or toggle feature flags — this limits blast radius if the key is compromised.Metadata safety:
queryHash) instead of raw user text.What data is transmitted:
What is NOT transmitted:
npm install @drip-sdk/node
# Recommended: public key — sufficient for all usage tracking and billing
export DRIP_API_KEY=pk_live_...
# Only if you need admin operations (webhooks, key management, feature flags):
# export DRIP_API_KEY=sk_live_...
queryHash) over raw user content.DRIP_BASE_URL.import { Drip } from '@drip-sdk/node';
// Reads DRIP_API_KEY from environment automatically (pk_live_... recommended)
const drip = new Drip({
apiKey: process.env.DRIP_API_KEY
});
await drip.trackUsage({
customerId: 'customer_123',
meter: 'llm_tokens',
quantity: 1500,
// metadata is optional — only include operational context, never PII or secrets
metadata: { model: 'gpt-4' }
});
await drip.recordRun({
customerId: 'cus_123',
workflow: 'research-agent',
events: [
{ eventType: 'llm.call', model: 'gpt-4', quantity: 1700, units: 'tokens' },
{ eventType: 'tool.call', name: 'web-search', duration: 1500 },
{ eventType: 'llm.call', model: 'gpt-4', quantity: 1000, units: 'tokens' },
],
status: 'COMPLETED',
});
// Start the run
const run = await drip.startRun({
customerId: 'cus_123',
workflowSlug: 'document-processor',
});
// Log each step as it happens
await drip.emitEvent({
runId: run.id,
eventType: 'llm.call',
model: 'gpt-4',
quantity: 1700,
units: 'tokens',
});
await drip.emitEvent({
runId: run.id,
eventType: 'tool.call',
name: 'web-search',
duration: 1500,
});
// Complete the run
await drip.endRun(run.id, { status: 'COMPLETED' });
| Event Type | Description | Key Fields |
|---|---|---|
llm.call | LLM API call | model, quantity, units |
tool.call | Tool invocation | name, duration, status |
agent.plan | Planning step | description |
agent.execute | Execution step | description, metadata |
error | Error occurred | description, metadata |
async function trackedToolCall<T>(runId: string, toolName: string, fn: () => Promise<T>): Promise<T> {
const start = Date.now();
try {
const result = await fn();
await drip.emitEvent({
runId,
eventType: 'tool.call',
name: toolName,
duration: Date.now() - start,
status: 'success',
});
return result;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Unknown error';
await drip.emitEvent({
runId,
eventType: 'tool.call',
name: toolName,
duration: Date.now() - start,
status: 'error',
// Only include the error message — never include stack traces, env vars, or user data
metadata: { error: message },
});
throw error;
}
}
import { DripCallbackHandler } from '@drip-sdk/node/langchain';
const handler = new DripCallbackHandler({
drip,
customerId: 'cus_123',
workflow: 'research-agent',
});
// All LLM calls and tool usage automatically tracked
const result = await agent.invoke(
{ input: 'Research the latest AI news' },
{ callbacks: [handler] }
);
See references/API.md for complete SDK documentation.