Customers API
Customers represent users of your application. Each customer:
- Has an external ID (your system’s user identifier)
- Can have custom metadata
- May have a subscription to a tier (which defines model access and usage limits)
Quick Reference
GET /api/v1/customers List customers
POST /api/v1/customers Create customer
GET /api/v1/customers/:id Get customer
PUT /api/v1/customers Upsert customer by external_id
DELETE /api/v1/customers/:id Delete customer
POST /api/v1/customers/:id/subscribe Create checkout session
GET /api/v1/customers/:id/subscription Get subscription status
POST /api/v1/customers/claim Link identity to customer
Authentication
Customer management endpoints require a secret key (mr_sk_*).
The /customers/claim endpoint also accepts publishable keys for user self-service flows.
List Customers
GET /api/v1/customers
Returns all customers in the project.
If a customer is not subscribed, the subscription field is null.
Response
{
"customers": [
{
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"project_id": "550e8400-e29b-41d4-a716-446655440001",
"external_id": "user_123",
"email": "alice@example.com",
"metadata": {
"plan": "monthly",
"referral_code": "FRIEND10"
},
"created_at": "2024-01-01T10:30:00Z",
"updated_at": "2024-01-15T14:22:00Z"
},
"subscription": {
"id": "550e8400-e29b-41d4-a716-446655440010",
"project_id": "550e8400-e29b-41d4-a716-446655440001",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"tier_id": "550e8400-e29b-41d4-a716-446655440002",
"tier_code": "pro",
"subscription_status": "active",
"current_period_start": "2024-01-01T00:00:00Z",
"current_period_end": "2024-02-01T00:00:00Z",
"created_at": "2024-01-01T10:30:00Z",
"updated_at": "2024-01-15T14:22:00Z"
}
}
]
}
Example
curl https://api.modelrelay.ai/api/v1/customers \
-H "Authorization: Bearer mr_sk_..."
import { ModelRelay } from "@modelrelay/sdk";
const mr = ModelRelay.fromSecretKey(process.env.MODELRELAY_SECRET_KEY!);
const customers = await mr.customers.list();
for (const entry of customers) {
const tier = entry.subscription?.tier_code ?? "none";
console.log(`${entry.customer.external_id}: ${tier}`);
}
customers, err := client.Customers.List(ctx)
if err != nil {
return err
}
for _, entry := range customers {
tier := "none"
if entry.Subscription != nil {
tier = entry.Subscription.TierCode.String()
}
fmt.Printf("%s: %s\n", entry.Customer.ExternalID, tier)
}
Create Customer
POST /api/v1/customers
Creates a new customer in the project.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
external_id |
string | Yes | Your system’s user identifier (1-255 chars) |
email |
string | Yes | Customer email address |
metadata |
object | No | Custom key-value data (max 10KB) |
Response
Returns the created customer object wrapped in { "customer": ... }.
Example
curl -X POST https://api.modelrelay.ai/api/v1/customers \
-H "Authorization: Bearer mr_sk_..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "user_456",
"email": "bob@example.com",
"metadata": {
"signup_source": "landing_page"
}
}'
const customer = await mr.customers.create({
external_id: "user_456",
email: "bob@example.com",
metadata: {
signup_source: "landing_page",
},
});
console.log(`Created customer: ${customer.customer.id}`);
customer, err := client.Customers.Create(ctx, sdk.CustomerCreateRequest{
ExternalID: sdk.NewCustomerExternalID("user_456"),
Email: "bob@example.com",
Metadata: sdk.CustomerMetadata{
"signup_source": "landing_page",
},
})
if err != nil {
return err
}
fmt.Printf("Created customer: %s\n", customer.Customer.ID)
Get Customer
GET /api/v1/customers/:id
Returns a single customer by ID.
Example
curl https://api.modelrelay.ai/api/v1/customers/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer mr_sk_..."
const customer = await mr.customers.get("550e8400-e29b-41d4-a716-446655440000");
const tier = customer.subscription?.tier_code ?? "none";
console.log(`${customer.customer.external_id} is on the ${tier} tier`);
customer, err := client.Customers.Get(ctx, uuid.MustParse("550e8400-e29b-41d4-a716-446655440000"))
if err != nil {
return err
}
tier := "none"
if customer.Subscription != nil {
tier = customer.Subscription.TierCode.String()
}
fmt.Printf("%s is on the %s tier\n", customer.Customer.ExternalID, tier)
Upsert Customer
PUT /api/v1/customers
Creates or updates a customer by external_id. If a customer with the given external_id exists, it is updated. Otherwise, a new customer is created.
This is useful for syncing customers from your user database without checking if they exist first.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
external_id |
string | Yes | Your system’s user identifier |
email |
string | Yes | Customer email address |
metadata |
object | No | Custom key-value data |
Example
curl -X PUT https://api.modelrelay.ai/api/v1/customers \
-H "Authorization: Bearer mr_sk_..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "user_456",
"email": "bob@newdomain.com"
}'
// Creates if not exists, updates if exists
const customer = await mr.customers.upsert({
external_id: "user_456",
email: "bob@newdomain.com",
});
customer, err := client.Customers.Upsert(ctx, sdk.CustomerUpsertRequest{
ExternalID: sdk.NewCustomerExternalID("user_456"),
Email: "bob@newdomain.com",
})
Delete Customer
DELETE /api/v1/customers/:id
Removes a customer. This does not cancel their Stripe subscription automatically.
Example
curl -X DELETE https://api.modelrelay.ai/api/v1/customers/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer mr_sk_..."
await mr.customers.delete("550e8400-e29b-41d4-a716-446655440000");
err := client.Customers.Delete(ctx, uuid.MustParse("550e8400-e29b-41d4-a716-446655440000"))
Create Checkout Session
POST /api/v1/customers/:id/subscribe
Creates a Stripe checkout session for an existing customer to subscribe to their tier’s paid plan.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
tier_id |
string | Yes | Tier to subscribe the customer to |
success_url |
string | Yes | URL to redirect after successful payment |
cancel_url |
string | Yes | URL to redirect if user cancels |
Response
| Field | Type | Description |
|---|---|---|
session_id |
string | Stripe checkout session ID |
url |
string | URL to redirect user to Stripe checkout |
Example
curl -X POST https://api.modelrelay.ai/api/v1/customers/550e8400-e29b-41d4-a716-446655440000/subscribe \
-H "Authorization: Bearer mr_sk_..." \
-H "Content-Type: application/json" \
-d '{
"tier_id": "550e8400-e29b-41d4-a716-446655440111",
"success_url": "https://myapp.com/success",
"cancel_url": "https://myapp.com/billing"
}'
const session = await mr.customers.subscribe(
"550e8400-e29b-41d4-a716-446655440000",
{
tier_id: "550e8400-e29b-41d4-a716-446655440111",
success_url: "https://myapp.com/success",
cancel_url: "https://myapp.com/billing",
}
);
// Redirect user to Stripe
res.redirect(session.url);
session, err := client.Customers.Subscribe(ctx,
uuid.MustParse("550e8400-e29b-41d4-a716-446655440000"),
sdk.CustomerSubscribeRequest{
TierID: uuid.MustParse("550e8400-e29b-41d4-a716-446655440111"),
SuccessURL: "https://myapp.com/success",
CancelURL: "https://myapp.com/billing",
},
)
if err != nil {
return err
}
http.Redirect(w, r, session.URL, http.StatusSeeOther)
Get Subscription Status
GET /api/v1/customers/:id/subscription
Returns the Stripe subscription status for a customer.
Response
| Field | Type | Description |
|---|---|---|
active |
boolean | Whether the subscription is active |
subscription_id |
string | Stripe subscription ID |
status |
string | Subscription status (active, trialing, past_due, etc.) |
current_period_start |
string | Start of current billing period (ISO 8601) |
current_period_end |
string | End of current billing period (ISO 8601) |
Example
curl https://api.modelrelay.ai/api/v1/customers/550e8400-e29b-41d4-a716-446655440000/subscription \
-H "Authorization: Bearer mr_sk_..."
const subscription = await mr.customers.getSubscription(
"550e8400-e29b-41d4-a716-446655440000"
);
if (subscription.active) {
console.log(`Active until ${subscription.current_period_end}`);
}
sub, err := client.Customers.GetSubscription(ctx,
uuid.MustParse("550e8400-e29b-41d4-a716-446655440000"),
)
if err != nil {
return err
}
if sub.Active {
fmt.Printf("Active until %s\n", sub.CurrentPeriodEnd)
}
Claim Customer
POST /api/v1/customers/claim
Links a customer identity (OAuth provider + subject) to a customer found by email. Used when a customer subscribes via Stripe Checkout (email only) and later authenticates to your app.
This endpoint works with both publishable and secret keys, enabling user self-service flows.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Email to match the customer |
provider |
string | Yes | OAuth provider (e.g., github, google) |
subject |
string | Yes | Provider’s user identifier |
Response
Returns the claimed customer object.
Example
curl -X POST https://api.modelrelay.ai/api/v1/customers/claim \
-H "Authorization: Bearer mr_pk_..." \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"provider": "github",
"subject": "12345678"
}'
// After user authenticates with OAuth
const customer = await mr.customers.claim({
email: user.email,
provider: "github",
subject: user.id,
});
customer, err := client.Customers.Claim(ctx, sdk.CustomerClaimRequest{
Email: user.Email,
Provider: sdk.CustomerIdentityProvider("github"),
Subject: sdk.CustomerIdentitySubject(user.ID),
})
Customer Object
| Field | Type | Description |
|---|---|---|
id |
uuid | Unique customer identifier |
project_id |
uuid | Project this customer belongs to |
external_id |
string | Your system’s user identifier |
email |
string | Customer email address |
metadata |
object | Custom key-value data |
created_at |
datetime | When the customer was created |
updated_at |
datetime | When the customer was last modified |
Subscription Object
| Field | Type | Description |
|---|---|---|
id |
uuid | Unique subscription identifier |
project_id |
uuid | Project this subscription belongs to |
customer_id |
uuid | Customer this subscription is attached to |
tier_id |
uuid | Tier this subscription grants access to |
tier_code |
string | Tier code (e.g., free, pro) |
billing_provider |
string | Billing provider (stripe, crypto, app_store, external) |
billing_customer_id |
string | Billing customer ID from the provider |
billing_subscription_id |
string | Billing subscription ID from the provider |
subscription_status |
string | Subscription status |
current_period_start |
datetime | Start of current billing period |
current_period_end |
datetime | End of current billing period |
created_at |
datetime | When the subscription was created |
updated_at |
datetime | When the subscription was last modified |
Subscription Status Values
| Status | Description |
|---|---|
active |
Subscription is active and paid |
trialing |
Customer is in free trial period |
past_due |
Payment failed, grace period active |
canceled |
Subscription was canceled |
unpaid |
Payment failed, subscription suspended |
incomplete |
Initial payment pending |
incomplete_expired |
Initial payment expired |
paused |
Subscription paused |
Metadata
Customer metadata allows storing arbitrary key-value data with each customer. Common use cases:
- User preferences
- Feature flags
- Referral tracking
- Integration IDs
Constraints
| Constraint | Limit |
|---|---|
| Total size | 10 KB |
| Key length | 40 characters |
| Nesting depth | 5 levels |
| Value types | string, number, boolean, null, array, object |
Example
{
"metadata": {
"plan": "annual",
"referral_code": "FRIEND10",
"feature_flags": {
"beta_access": true,
"dark_mode": false
},
"integrations": ["slack", "github"]
}
}
Customer Tokens
To make API requests on behalf of a customer (for /responses, /runs endpoints), mint a customer token:
POST /api/v1/auth/customer-token
See Authentication for details on customer tokens.
Error Codes
| Status | Description |
|---|---|
| 400 | Invalid request (missing fields, invalid email) |
| 401 | Missing or invalid authentication |
| 404 | Customer not found |
| 409 | Identity already linked to a different customer (claim) |
Next Steps
- Tiers API - Configure pricing tiers
- Authentication - Customer tokens
- Responses API - Make AI requests