Customers
The Customer API Resource
Below you'll find all properties for the Vatly Customer API resource.
Properties
| Name | Type | Description |
|---|---|---|
id | string | Unique identifier for the customer (starts with customer_). |
resource | string | The resource type. Always customer. |
testmode | boolean | Whether this resource is in test mode. |
email | string | The customer's email address. Internationalized domains are stored and returned in their Punycode (ASCII) form per UTS #46 — e.g. user@xn--mller-kva.de. Convert back to Unicode for display in your own UI. |
name | string | null | The customer's display / account-holder name. An identity field used for communication (dunning emails, dashboard) — distinct from, and with no effect on, the billing name on invoices. |
createdAt | string | When this customer was created (ISO 8601 format). |
metadata | object | null | Arbitrary key-value metadata for your application. Up to 50 keys, with key names up to 40 characters and values up to 500 characters. |
links | object | HATEOAS links to related resources. Contains self link. |
List all customers
GET /v1/customers
This endpoint allows you to retrieve a paginated list of all your customers. By default, a maximum of ten customers are shown per page.
Pass email to filter the list down to the customers holding an address — the way back to a customer id you no longer have.
Optional attributes
| Name | Type | Description |
|---|---|---|
email | string | Return customers with this email address, within the storefront and mode the API token is scoped to. Canonicalized before matching, exactly as on write. Beyond that the comparison is exact, so büyer@example.com does not match buyer@example.com. An address can be held by more than one customer, in which case all of them are returned. A value that is not a valid email address returns 422. |
limit | integer | The number of customers to return (default: 10, max: 100). |
startingAfter | string | A cursor for use in pagination. Returns results after this customer ID. |
endingBefore | string | A cursor for use in pagination. Returns results before this customer ID. |
curl -G https://api.vatly.com/v1/customers \
-H "Authorization: Bearer live_your_api_key_here" \
-d email=john.doe@example.com
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$customers = $vatly->customers->page(['email' => 'john.doe@example.com']);
{
"data": [
{
"id": "customer_7kBmRtPvXw2NjLhYcZaE",
"resource": "customer",
"testmode": false,
"email": "john.doe@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
},
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE",
"type": "application/json"
}
}
},
{
"id": "customer_Lp3mNvBxKw7RjTgYcZaE",
"resource": "customer",
"testmode": false,
"email": "jane.smith@acme.com",
"name": "Jane Smith",
"createdAt": "2024-01-10T08:15:00Z",
"metadata": {},
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
}
],
"count": 2,
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers",
"type": "application/json"
},
"next": null,
"prev": null
}
}
Create a customer
POST /v1/customers
Creates a customer, or returns the one that already holds the address.
Customers are uniquely identified by email within a storefront and testmode, and this endpoint is get-or-create on that key: posting an address that already exists returns the existing customer rather than a validation error, so an integration that lost its Vatly customer id can always get back to it. The status code says which happened:
| Status | Meaning |
|---|---|
201 | The customer was created by this request. |
200 | A customer with this email already existed and is returned unchanged. |
A 200 response never modifies the customer — name and metadata in the request body are ignored for an existing customer. Use PATCH /v1/customers/:id to change one.
A 201 can also complete a create that was interrupted before it finished, in which case the customer keeps the id and createdAt of that earlier attempt — so createdAt may predate the request. Treat it as when the customer came into being, not as when this call happened.
Addresses are canonicalized before they are compared, so User@MÜLLER.de and user@xn--mller-kva.de are the same customer. Beyond that the comparison is exact — büyer@example.com is a different mailbox and gets its own customer.
Required attributes
| Name | Type | Description |
|---|---|---|
email | string | The email address for the customer. Must be unique within the merchant's account for the given testmode. |
Optional attributes
| Name | Type | Description |
|---|---|---|
name | string | The customer's display / account-holder name (max 255 characters). |
metadata | object | Arbitrary key-value metadata for your application. |
curl https://api.vatly.com/v1/customers \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"email": "customer@example.com", "metadata": {"userId": "user_Qp8kNvBxKw7RjTgYcZaE"}}'
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$vatly->customers->create([
'email' => 'customer@example.com',
'metadata' => [
'userId' => 'user_Qp8kNvBxKw7RjTgYcZaE',
],
]);
{
"id": "customer_7kBmRtPvXw2NjLhYcZaE",
"resource": "customer",
"testmode": false,
"email": "customer@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
},
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE",
"type": "application/json"
}
}
}
Retrieve a customer
GET /v1/customers/:id
This endpoint allows you to retrieve a customer by providing their Vatly id. Refer to the list at the top of this page to see which properties are included with customer objects.
curl https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE \
-H "Authorization: Bearer live_your_api_key_here"
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$vatly->customers->get('customer_7kBmRtPvXw2NjLhYcZaE');
{
"id": "customer_7kBmRtPvXw2NjLhYcZaE",
"resource": "customer",
"testmode": false,
"email": "john.doe@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
},
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE",
"type": "application/json"
}
}
}
Update a customer
PATCH /v1/customers/:id
Updates a customer's identity fields. Only name and email may be changed here, and both are optional — send whichever you want to update.
Billing-address details (company name, tax ID, street, city, country, etc.) are not supported by this endpoint and are ignored. Amend those through the hosted billing-update flow, which validates tax-relevant data centrally so invoices stay accurate.
Optional attributes
| Name | Type | Description |
|---|---|---|
name | string | null | The customer's display / account-holder name (max 255 characters). |
email | string | The customer's email address. Must be unique within the merchant's account for the given testmode. |
curl -X PATCH https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "new.email@example.com"}'
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$customer = $vatly->customers->update('customer_7kBmRtPvXw2NjLhYcZaE', [
'name' => 'John Doe',
'email' => 'new.email@example.com',
]);
{
"id": "customer_7kBmRtPvXw2NjLhYcZaE",
"resource": "customer",
"testmode": false,
"email": "new.email@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
},
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE",
"type": "application/json"
}
}
}
Create a customer portal session
POST /v1/customers/:customerId/portal-sessions
Creates a short-lived, single-use link that sends an authenticated customer straight to this API token's storefront in Vatly's hosted portal. The session is locked to the customer, storefront, merchant, and live/test mode represented by the token; it never exposes a storefront picker or other stores associated with the same email address. For a merchant with multiple storefronts, each API token issues portal links for its own storefront.
Redirect the customer's browser to url. The link expires after roughly 15 minutes by default and can be consumed once. If an idempotency key replays a response after its URL has already been used, request a replacement with a fresh key.
The response is credential-bearing, so it is returned with Cache-Control: no-store, private. Do not log or store the url.
Optional attributes
| Name | Type | Description |
|---|---|---|
returnUrl | string | null | Absolute HTTPS URL without embedded credentials, rendered as a return link in the hosted portal (max 2048 bytes). Vatly never fetches or automatically redirects to this URL. |
Response
| Name | Type | Description |
|---|---|---|
url | string | Single-use HTTPS URL to redirect the customer to. |
expiresAt | string | Expiry of the one-time entry link, not of the resulting browser session (ISO 8601 format). |
returnUrl | string | null | The validated absolute HTTPS return URL supplied in the request, or null. |
curl https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE/portal-sessions \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"returnUrl": "https://merchant.example/account/billing"}'
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$vatly->customers->createPortalSession('customer_7kBmRtPvXw2NjLhYcZaE', [
'returnUrl' => 'https://merchant.example/account/billing',
]);
{
"url": "https://billing.vatly.com/authenticate?credential=...",
"expiresAt": "2026-09-01T12:15:00Z",
"returnUrl": "https://merchant.example/account/billing"
}