Customers

Customers represent the people or organizations who purchase licenses. Each license is associated with a customer within your application.

Customer Properties

Field Type Description
email string Customer's email address (unique per app)
name string? Optional customer name
external_id string? External system ID (e.g., Stripe customer ID)
metadata object? JSON object for custom data

Creating Customers

POST /api/v1/apps/:slug/customers
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/customers \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "name": "Jane Smith",
    "external_id": "cus_stripe_123",
    "metadata": {
      "company": "Acme Corp",
      "plan": "annual"
    }
  }'

# Response
{
  "data": {
    "id": "cus_abc123",
    "email": "jane@example.com",
    "name": "Jane Smith",
    "external_id": "cus_stripe_123",
    "metadata": {"company": "Acme Corp", "plan": "annual"},
    "created_at": "2026-01-23T10:00:00Z"
  }
}

Get or Create

For integration workflows, use the get-or-create pattern to idempotently ensure a customer exists:

POST /api/v1/apps/:slug/customers/get-or-create
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/customers/get-or-create \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "name": "Jane Smith"
  }'

# Returns existing customer if email matches,
# otherwise creates a new one

Filtering & Pagination

List customers with optional filters:

GET /api/v1/apps/:slug/customers
# Filter by email (partial match)
$ curl "http://localhost:4000/api/v1/apps/my-app/customers?email=jane" \
  -H "Authorization: Bearer sk_live_..."

# Filter by external_id (exact match)
$ curl "http://localhost:4000/api/v1/apps/my-app/customers?external_id=cus_stripe_123" \
  -H "Authorization: Bearer sk_live_..."

# Paginate results
$ curl "http://localhost:4000/api/v1/apps/my-app/customers?page=1&per_page=20" \
  -H "Authorization: Bearer sk_live_..."

# Response includes pagination meta
{
  "data": [...],
  "meta": {
    "total": 150,
    "page": 1,
    "per_page": 20
  }
}

Customer Lookup

Retrieve customers by ID, email, or external_id:

GET endpoints
# By ID
$ curl http://localhost:4000/api/v1/apps/my-app/customers/cus_abc123 \
  -H "Authorization: Bearer sk_live_..."

# By email
$ curl "http://localhost:4000/api/v1/apps/my-app/customers/by-email?email=jane@example.com" \
  -H "Authorization: Bearer sk_live_..."

# By external_id
$ curl "http://localhost:4000/api/v1/apps/my-app/customers/by-external-id?external_id=cus_stripe_123" \
  -H "Authorization: Bearer sk_live_..."

Cascade Deletion

When a customer is deleted, all their licenses are also deleted. When an app is deleted, all customers and licenses are deleted.

Warning: Customer deletion is permanent and cascades to all licenses. Consider revoking licenses instead if you want to preserve history.