Get Customer
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
Section titled “Request”GET /cdp/customers/{identifier}Authorization: Bearer vyg_…Path parameters
Section titled “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
Section titled “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
Section titled “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
Section titled “Example”curl -s "https://cdp.vyg.app/cdp/customers/alice@example.com" \ -H "Authorization: Bearer vyg_your_key_here"Errors
Section titled “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.