Audit trail
Every write in Driptab is recorded. This page explains the two audit tables, what they capture, and how to query them.
Two tables, two purposes
billing_events — domain event log
billing_events records every mutation to a billing resource. One row per event.
| Field | Description |
|---|---|
id |
UUIDv4 |
organization_id |
Tenant ID |
project_id |
Project ID (most resources are project-scoped) |
resource_type |
Type of affected object: customer, plan, subscription, invoice, etc. |
resource_id |
UUID of the affected object |
event_type |
Dotted code: customer.created, plan.archived, subscription.terminated, etc. |
actor_token_id |
ID of the API token that caused the event. Null for system-generated events. |
payload |
JSON snapshot of relevant context at the time of the event |
created_at |
When the event was recorded |
Example row:
{
"id": "...",
"organization_id": "org-uuid",
"project_id": "proj-uuid",
"resource_type": "customer",
"resource_id": "cust-uuid",
"event_type": "customer.created",
"actor_token_id": "token-uuid",
"payload": { "external_id": "emailfs_account_abc123", "name": "Ali Yilmaz" },
"created_at": "2026-06-08T02:57:35.000Z"
}
audit_logs — HTTP access log
audit_logs records every API request, whether it succeeded or failed.
| Field | Description |
|---|---|
id |
UUIDv4 |
organization_id |
Tenant ID |
token_id |
ID of the token that made the request |
method |
HTTP method: GET, POST, DELETE |
path |
Request path: /v1/customers |
status |
HTTP response status code |
duration_ms |
Response time in milliseconds |
ip |
Caller IP address |
created_at |
When the request was received |
What billing_events covers
Billing events are emitted for every resource mutation:
| Event type | Trigger |
|---|---|
customer.created |
POST /v1/customers |
plan.created |
POST /v1/plans |
plan.archived |
DELETE /v1/plans/:id |
charge.created |
POST /v1/plans/:id/charges |
catalog_item.upserted |
POST /v1/catalog |
catalog_item.archived |
DELETE /v1/catalog/:id |
subscription.created |
POST /v1/subscriptions |
subscription.terminated |
POST /v1/subscriptions/:id/terminate |
invoice.created |
Period close (DO alarm) |
payment.recorded |
POST /v1/payments |
addon.applied |
POST /v1/addons/:id/apply |
metric.created |
POST /v1/metrics |
System-generated events (like invoice.created from the DO alarm) have
actor_token_id: null.
Immutability guarantees
Both tables are append-only:
- No
UPDATEis ever run against them. - No row is deleted, even if the referenced resource is later archived or the token is revoked.
- Cascade deletes are not enabled — historical records survive resource deletion.
This makes the audit trail suitable for compliance and dispute resolution.
Querying via the API
GET /v1/billing-events?resource_type=customer&resource_id=<uuid>
GET /v1/billing-events?event_type=plan.archived
GET /v1/billing-events?actor_token_id=<token_id>
dtab audit events --resource-type customer --resource-id <id>
dtab audit events --event-type plan.archived --since 2026-06-01
Responses are paginated with a cursor field.
Actor attribution
Every billing event records the actor_token_id. This lets you answer:
- "Which API token deleted this plan?"
- "Which integration created these subscriptions?"
- "What has this MCP agent token done today?"
Combine with the token list (GET /v1/keys) to map token IDs to names.
Retention
Billing events should be retained indefinitely for financial compliance. Audit logs
can be retained on a shorter schedule (e.g. 90 days). When billing_events grows
large, archive older rows to R2 and query them via the R2 export format.