Get Customer
Fetch a unified customer record — CDP profile, commerce summary, connected integrations, and subscription — by profile id, email, phone, Shopify customer id, or contact id.
GET /cdp/customers/{identifier} — fetch a single unified customer record that
merges a customer's behavioral CDP profile (from the CDP) with their commerce
history (from the local synced store), joined by identity. One call returns the
profile core, a commerce summary, the presence of your connected integrations, and
subscription status.
Requires the beta:cdp-commerce permission on your brand.
Request
GET /cdp/customers/{identifier}
Authorization: Bearer vyg_…Path parameters
| Param | Type | Description |
|---|---|---|
identifier | string | A profile id, email, phone, Shopify customer id, or contact id. Classified by shape below. |
The identifier is disambiguated by shape, deterministically:
| Shape | Interpreted as |
|---|---|
contains @ | email |
starts with + | phone |
all digits (^\d+$) | shopify_customer_id |
| a UUID | profile_id, then contact_id |
| anything else | profile_id (a CDP profile itemId) |
A UUID is first tried as a CDP profile_id (a CDP profile itemId); if no
profile matches, it is then tried as a contact_id — the local contacts.id that
Customer Orders and List Orders
return as customer.contact_id. This lets you join an order straight back to its
customer with the id you already have. The CDP profile always takes precedence;
matched_by tells you which resolved.
Response 200
{
"identifier": "alice@example.com",
"matched_by": "email",
"scope": "your-shop.myshopify.com",
"profile": {
"id": "shopify_your-shop_5483611717768",
"provenance": "server",
"identity": {
"email": "alice@example.com",
"phoneNumber": "+14155550123",
"firstName": "Alice",
"shopifyCustomerId": "5483611717768",
"shopDomain": "your-shop.myshopify.com"
},
"segments": ["vip", "repeat-buyer"],
"behavior": {
"nb_of_visits": 12,
"first_visit": "2026-01-04T10:15:00.000Z",
"last_visit": "2026-06-30T18:02:00.000Z"
}
},
"commerce": {
"total_spend": "200.00",
"currency_code": "USD",
"order_count": 2,
"aov": "100.00",
"first_order_at": "2026-02-20T00:00:00.000Z",
"last_order_at": "2026-03-02T00:00:00.000Z"
},
"integrations": [
{
"id": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"integration_id": "d1e3f5a7-2b4c-6d8e-0f1a-3c5e7b9d1f23",
"is_enabled": true,
"status": "connected",
"connected_at": "2026-01-01T00:00:00.000Z"
}
],
"subscription": {
"status": "active",
"active_count": 1,
"total_count": 1,
"next_billing_date": "2026-08-01T00:00:00.000Z"
}
}| Field | Type | Description |
|---|---|---|
identifier | string | The identifier you requested, echoed back. |
matched_by | string | Which identity key resolved the local commerce contact: email, shopify_customer_id, phone, contact_id (a direct contacts.id match), or none. |
scope | string | null | Your brand's connected shop domain. |
profile | object | null | The CDP profile core, or null when no in-scope profile matched (commerce-only customer). |
profile.provenance | string | server, pixel, or merged — how the profile entered the CDP. |
profile.identity | object | Projected identity fields (email, phone, name, Shopify customer id, shop domain). |
profile.segments | array | The CDP segment ids the profile is a member of. |
profile.behavior | object | Behavioral rollups: nb_of_visits, first_visit, last_visit (each null when absent). |
commerce | object | null | Commerce summary over the customer's completed orders, or null when no commerce contact resolved. |
commerce.total_spend / aov | string | Fixed-2 decimal money strings. aov = total_spend / order_count. |
commerce.order_count | integer | Count of completed orders. |
commerce.first_order_at / last_order_at | string | null | Earliest / latest completed-order timestamps. |
integrations | array | Presence of your connected integrations — each entry carries id, integration_id, is_enabled, status, and connected_at only. integration_id is an opaque integration id (a UUID), not a provider name; resolve it to a provider via List Integrations. No credentials or settings are ever returned. |
subscription | object | status (active / inactive / none), active_count, total_count, next_billing_date. |
total_spend, order_count, and aov are computed over completed orders
only (matching the platform's GMV definition).
Partial matches degrade cleanly
The profile and commerce halves are joined but independent, so a partial match
returns the half that exists with null on the other:
- A profile with no matching orders →
commerce: null,subscription.status: "none", andmatched_by: "none"— a200, never an error. - Orders with no profile →
profile: null, withcommercepopulated. This is the shape you get when you look a customer up by theircontact_id(from an order):matched_by: "contact_id", no CDP profile, commerce populated.
Example
curl -s "https://cdp.vyg.app/cdp/customers/alice@example.com" \
-H "Authorization: Bearer vyg_your_key_here"Errors
| Status | When |
|---|---|
400 | Missing identifier. |
401 | Missing or invalid credential. |
403 | Brand not enrolled in the commerce beta (beta_not_enabled), or no connected Shopify integration (integration_not_connected). |
404 | The customer is absent from both stores — an unknown identifier or one belonging to another brand, indistinguishable by design so a brand cannot probe for another brand's customers. |
405 | Non-GET method. |
{
"error": "not_found",
"error_description": "Customer not found"
}See Customer Orders for the customer's order
history, and Tenant Isolation & Scope for why
out-of-scope identifiers return 404.