Customers
Customers are the end users tracked in Tether. Use this endpoint to upsert customers by email or external ID.
The customer object
1{
2 "tether_id": "3f4e2b1a-...",
3 "external_id": "cus_abc123",
4 "email": "jane@acme.com",
5 "name": "Jane Doe",
6 "company_name": "Acme Corp",
7 "stripe_customer_id": "cus_stripe123",
8 "mrr": 99,
9 "subscription_status": "active",
10 "auto_renew": true,
11 "period_end": "2026-12-31T00:00:00Z",
12 "last_activity": "2026-04-01T09:00:00Z",
13 "created_at": "2025-01-15T10:00:00Z",
14 "updated_at": "2026-04-01T08:30:00Z"
15}| Name | Type | Required | Description |
|---|---|---|---|
| tether_id | string (UUID) | optional | Tether's internal identifier for this customer. Stable and unique across your account. |
| external_id | string | null | optional | Your own system's identifier for this customer. Set on upsert; use with X-Customer-Id-Type: external for lookups. |
| string | optional | The customer's email address (always lowercase). | |
| name | string | null | optional | Full name. |
| company_name | string | null | optional | Company or organization name. |
| stripe_customer_id | string | null | optional | Stripe customer ID. Set automatically when Tether is connected to Stripe. Read-only via the API. |
| mrr | number | null | optional | Monthly recurring revenue in your account's currency. |
| subscription_status | string | null | optional | Subscription state. Common values: "active", "trialing", "past_due", "canceled". |
| auto_renew | boolean | null | optional | Whether the subscription renews automatically at period end. |
| period_end | string | null (ISO 8601) | optional | End date of the current billing period. |
| last_activity | string | null (ISO 8601) | optional | Timestamp of the customer's last known activity. |
| created_at | string (ISO 8601) | optional | When the customer was added to Tether. |
| updated_at | string (ISO 8601) | optional | When the customer record was last modified. |
The X-Customer-Id-Type header
Several endpoints accept a customer identifier in the URL or request body. The X-Customer-Id-Type header tells Tether which identifier you are using:
| Name | Type | Required | Description |
|---|---|---|---|
| header value | optional | Look up the customer by email address. This is the default when the header is omitted. | |
| external | header value | optional | Look up the customer by your own system's ID, stored in the external_id field. Falls back to email lookup if no match is found. |
Upsert a customer
Creates or updates a customer. Tether looks up the customer using the identifier you provide — if found, it updates the record; if not found, it creates a new one (when creation is enabled for the product).
Requires a product-scoped API key. The key determines which product the customer belongs to.
Lookup behavior
The X-Customer-Id-Type header controls how Tether matches an existing customer:
- email (default) — matches on the
emailfield only. - external — matches on
external_idfirst; if no match, falls back toemail.
Customer creation gate
By default, the upsert endpoint will not create new customers — it will only update existing ones. To enable creation, go to Settings → Products → [product] → API and toggle Allow customer creation via API.
When creation is disabled and no matching customer is found, the API returns 422 CREATION_DISABLED.
Request body
At least one of email or external_id is required.
| Name | Type | Required | Description |
|---|---|---|---|
| string | optional | Customer email. Required when creating a new customer. | |
| external_id | string | optional | Your system's identifier for this customer. |
| name | string | optional | Full name. |
| company_name | string | optional | Company or organization name. |
| mrr | number | optional | Monthly recurring revenue. |
| subscription_status | string | optional | Subscription state. Example: "active", "canceled". |
| auto_renew | boolean | optional | Whether the subscription renews automatically. |
| period_end | string (ISO 8601) | optional | Current billing period end date. |
| last_activity | string (ISO 8601) | optional | Last known activity timestamp for this customer. |
Example request
async function request(data: Record<string, unknown>) {
const response = await fetch("https://usetether.io/api/v1/customers", {
method: "POST",
headers: {
Authorization: `Bearer ${import.meta.env.VITE_TETHER_API_KEY}`,
"X-Customer-Id-Type": "external",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "jane@acme.com",
external_id: "cus_abc123",
name: "Jane Doe",
mrr: 99,
subscription_status: "active",
}),
});
return response.json();
}Response — created (201)
1{
2 "action": "created",
3 "customer": {
4 "tether_id": "3f4e2b1a-...",
5 "external_id": "cus_abc123",
6 "email": "jane@acme.com",
7 "name": "Jane Doe",
8 "company_name": null,
9 "stripe_customer_id": null,
10 "mrr": 99,
11 "subscription_status": "active",
12 "auto_renew": null,
13 "period_end": null,
14 "last_activity": null,
15 "created_at": "2026-04-20T12:00:00Z",
16 "updated_at": "2026-04-20T12:00:00Z"
17 },
18 "health_score": 74
19}Response — updated (200)
1{
2 "action": "updated",
3 "customer": {
4 "tether_id": "3f4e2b1a-...",
5 "external_id": "cus_abc123",
6 "email": "jane@acme.com",
7 "name": "Jane Doe",
8 "company_name": null,
9 "stripe_customer_id": null,
10 "mrr": 99,
11 "subscription_status": "active",
12 "auto_renew": null,
13 "period_end": null,
14 "last_activity": null,
15 "created_at": "2025-01-15T10:00:00Z",
16 "updated_at": "2026-04-20T12:00:00Z"
17 },
18 "health_score": 74
19}Error — creation disabled (422)
1{
2 "error": "Customer not found and customer creation via API is disabled for this product. Enable it in Settings → Products → [product] → API.",
3 "code": "CREATION_DISABLED"
4}