Checkouts
The Checkout API Resource
Below you'll find all properties for the Vatly Checkout API resource.
Properties
| Name | Type | Description |
|---|---|---|
id | string | Unique identifier for the checkout (starts with checkout_). |
resource | string | The resource type. Always checkout. |
orderId | string | null | Unique identifier for the order created from this checkout. Only available when the checkout has been paid successfully. |
customerId | string | The customer associated with this checkout. Only present once a customer has been associated — for an anonymous checkout this happens when the buyer completes payment. |
testmode | boolean | Whether this checkout is in test mode. |
redirectUrlSuccess | string | The URL to which the checkout should redirect the user after the checkout has been paid successfully. May contain the literal {CHECKOUT_ID} placeholder, which Vatly substitutes with this checkout's ID at creation time (e.g. https://example.com/return?checkout_id={CHECKOUT_ID}). |
redirectUrlCanceled | string | The URL to which the user should get redirected when the user cancels the checkout. Supports the same {CHECKOUT_ID} placeholder as redirectUrlSuccess. |
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. |
locale | string | null | The language the hosted checkout was asked to present in, as sent on creation. null means none was specified and the checkout picks a language from the shopper's browser. One of en, de, fr, nl, es, it, pt, pl, or null. |
status | string | The status of the checkout. Can be created, paid, canceled, failed, or expired. |
expiresAt | string | null | When this checkout will expire (ISO 8601 format). |
createdAt | string | The moment the checkout was created, in ISO 8601 format. |
links | object | HATEOAS links to related resources. Contains checkoutUrl (the hosted checkout page URL), self, and optionally order (after completion). |
List all checkouts
GET /v1/checkouts
This endpoint allows you to retrieve a paginated list of all your checkouts. By default, a maximum of ten checkouts are shown per page.
Optional attributes
| Name | Type | Description |
|---|---|---|
limit | integer | The number of checkouts to return (default: 10, max: 100). |
startingAfter | string | A cursor for use in pagination. Returns results after this checkout ID. |
endingBefore | string | A cursor for use in pagination. Returns results before this checkout ID. |
curl -G https://api.vatly.com/v1/checkouts \
-H "Authorization: Bearer live_your_api_key_here" \
-d limit=10
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$checkouts = $vatly->checkouts->page();
{
"data": [
{
"id": "checkout_QdEpFhdSrG4Y3DnfsdqsH",
"resource": "checkout",
"orderId": null,
"testmode": false,
"redirectUrlSuccess": "https://example.com/success",
"redirectUrlCanceled": "https://example.com/canceled",
"metadata": {},
"locale": null,
"status": "created",
"expiresAt": "2024-01-16T10:30:00Z",
"createdAt": "2024-01-15T10:30:00Z",
"links": {
"checkoutUrl": {
"href": "https://checkout.vatly.com/checkout_QdEpFhdSrG4Y3DnfsdqsH",
"type": "text/html"
},
"self": {
"href": "https://api.vatly.com/v1/checkouts/checkout_QdEpFhdSrG4Y3DnfsdqsH",
"type": "application/json"
},
"order": null
}
}
],
"count": 1,
"links": {
"self": {
"href": "https://api.vatly.com/v1/checkouts",
"type": "application/json"
},
"next": null,
"prev": null
}
}
Create a checkout
POST /v1/checkouts
This endpoint allows you to start a new hosted Vatly Checkout. Make sure you have at least one subscription plan or one-off product configured in your Vatly account.
Once paid, any subscription plan product assigned to the checkout will kick off a new subscription for that plan.
Required attributes
| Name | Type | Description |
|---|---|---|
products | array | An array of product objects to include in this checkout. Each product can have: id (required, starts with one_off_product_ or subscription_plan_), quantity (optional, default: 1), price (optional, Money object), trialDays (optional, for subscription plans), metadata (optional). |
redirectUrlSuccess | string | The URL to which the checkout should redirect the user after the checkout has been paid successfully. You may include the literal {CHECKOUT_ID} placeholder anywhere in the URL — Vatly substitutes it with this checkout's ID, so your return page can read the checkout ID without a server-side token store. |
redirectUrlCanceled | string | The URL to which the user should get redirected when the user cancels the checkout. Supports the same {CHECKOUT_ID} placeholder. |
Optional attributes
| Name | Type | Description |
|---|---|---|
metadata | object | Arbitrary key-value metadata for your application. |
customerId | string | The ID for an existing customer to associate with this checkout. If provided, the customer's email will be pre-filled. Must match the testmode of the API token. |
locale | string | null | Language to present the hosted checkout in, including its validation and error messages. Send a bare language code (de), a BCP 47 tag (de-AT), or a POSIX / ISO 15897 locale (de_DE) — all three fold to the language, so there are no region-specific variants. The language also carries through to the payment provider's own hosted page. Supported languages: en, de, fr, nl, es, it, pt, pl; anything else is a 422 naming the supported set. Omit it (or send null) to detect the language from the shopper's Accept-Language header, falling back to English. The response echoes back the folded language, not the string you sent. |
curl https://api.vatly.com/v1/checkouts \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"products": [
{"id": "one_off_product_Vr8kQdFhSrG4Y3DnfsdqH", "quantity": 1},
{"id": "subscription_plan_Rk5pQrSvWm8NjLhYbUcP", "trialDays": 14}
],
"redirectUrlSuccess": "https://example.com/return?checkout_id={CHECKOUT_ID}",
"redirectUrlCanceled": "https://example.com/canceled",
"locale": "de"
}'
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$checkout = $vatly->checkouts->create([
'products' => [
['id' => 'one_off_product_Vr8kQdFhSrG4Y3DnfsdqH', 'quantity' => 1],
['id' => 'subscription_plan_Rk5pQrSvWm8NjLhYbUcP', 'trialDays' => 14],
],
'redirectUrlSuccess' => 'https://example.com/return?checkout_id={CHECKOUT_ID}',
'redirectUrlCanceled' => 'https://example.com/canceled',
'locale' => 'de',
]);
// Redirect the user to the checkout URL
header('Location: ' . $checkout->links->checkoutUrl->href, true, 303);
{
"id": "checkout_Bm7xNvPwKr3YjTgHcZaE",
"resource": "checkout",
"orderId": null,
"testmode": false,
"redirectUrlSuccess": "https://example.com/return?checkout_id=checkout_Bm7xNvPwKr3YjTgHcZaE",
"redirectUrlCanceled": "https://example.com/canceled",
"metadata": {},
"locale": "de",
"status": "created",
"expiresAt": "2024-01-16T10:30:00Z",
"createdAt": "2024-01-15T10:30:00Z",
"links": {
"checkoutUrl": {
"href": "https://checkout.vatly.com/checkout_Bm7xNvPwKr3YjTgHcZaE",
"type": "text/html"
},
"self": {
"href": "https://api.vatly.com/v1/checkouts/checkout_Bm7xNvPwKr3YjTgHcZaE",
"type": "application/json"
},
"order": null
}
}
Custom pricing
Charge an amount that differs from the product's dashboard price by setting price on the item — a Money object (value is a decimal string, currency an ISO 4217 code). The id still references a product you created in the dashboard; products cannot be created on the fly via the API. Omit price to use the product's configured price.
For a subscription plan, a custom price is not a first-cycle discount — it becomes the subscription's recurring price and carries over to every renewal. This is how you give a cohort a permanent price (for example, a lower price for an early group of customers). If you'd prefer to track that cohort separately, create a dedicated subscription plan at the target price and check out against that instead — the recurring price is the same, but reporting stays cleaner.
curl https://api.vatly.com/v1/checkouts \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"products": [
{
"id": "one_off_product_Vr8kQdFhSrG4Y3DnfsdqH",
"quantity": 2,
"price": {"value": "49.99", "currency": "EUR"}
}
],
"redirectUrlSuccess": "https://example.com/success",
"redirectUrlCanceled": "https://example.com/canceled"
}'
$checkout = $vatly->checkouts->create([
'products' => [
[
'id' => 'one_off_product_Vr8kQdFhSrG4Y3DnfsdqH',
'quantity' => 2,
'price' => ['value' => '49.99', 'currency' => 'EUR'],
],
],
'redirectUrlSuccess' => 'https://example.com/success',
'redirectUrlCanceled' => 'https://example.com/canceled',
]);
VAT and reverse charge
As Merchant of Record, Vatly determines the applicable tax when the checkout is paid, based on the buyer's country and tax status — you don't calculate or configure rates yourself. For EU B2B, the buyer's VAT ID is validated against the EU's VIES registry as part of that determination: when a valid VAT ID is supplied and the buyer is in a different country from the supplier, the reverse charge applies (0% VAT) and the resulting order reflects it. Otherwise the buyer's country standard rate applies.
Payment methods in test mode
A few payment methods work in live mode but cannot be completed in test mode, because the payment provider cannot settle them there. A test-mode checkout still lists them, greyed out and labelled as unavailable, so you can see they exist without being able to select one:
| Method | Test mode | Reason |
|---|---|---|
banktransfer | Not selectable | The provider cannot settle bank transfers in test mode. |
paybybank | Not selectable | The provider cannot settle Pay by Bank in test mode. |
Every other method behaves identically in both modes. Test a checkout with any selectable method — creditcard and ideal cover the one-off and mandate flows — and the resulting orders, invoices and webhooks are the same ones live mode produces.
Retrieve a checkout
GET /v1/checkouts/:id
This endpoint allows you to retrieve a checkout by providing the checkout id. Refer to the list at the top of this page to see which properties are included with checkout objects.
curl https://api.vatly.com/v1/checkouts/checkout_QdEpFhdSrG4Y3DnfsdqsH \
-H "Authorization: Bearer live_your_api_key_here"
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$checkout = $vatly->checkouts->get('checkout_QdEpFhdSrG4Y3DnfsdqsH');
{
"id": "checkout_QdEpFhdSrG4Y3DnfsdqsH",
"resource": "checkout",
"orderId": "order_Jk4pQrSvWm8NjLhYbUcP",
"testmode": false,
"redirectUrlSuccess": "https://example.com/success",
"redirectUrlCanceled": "https://example.com/canceled",
"metadata": {
"campaign": "summer-sale"
},
"locale": null,
"status": "paid",
"expiresAt": "2024-01-16T10:30:00Z",
"createdAt": "2024-01-15T10:30:00Z",
"links": {
"checkoutUrl": {
"href": "https://checkout.vatly.com/checkout_QdEpFhdSrG4Y3DnfsdqsH",
"type": "text/html"
},
"self": {
"href": "https://api.vatly.com/v1/checkouts/checkout_QdEpFhdSrG4Y3DnfsdqsH",
"type": "application/json"
},
"order": {
"href": "https://api.vatly.com/v1/orders/order_Jk4pQrSvWm8NjLhYbUcP",
"type": "application/json"
}
}
}