Replacing Checkout (Server-Side)
Use this approach when you want your server to redirect users to Simpler Checkout at the point where they would normally enter your CMS's native checkout.
If you don't need server-side control and just want a simple frontend redirect, see Replacing Checkout (Client-Side) instead.
This endpoint does not require any authentication headers. You only need your merchant_id (App ID).
Overview
The flow has three steps:
- Build a Cart Request object describing the user's cart.
- POST it to the Simpler endpoint.
- Forward the redirect response to the user's browser.
After the redirect, the user lands on the Simpler Checkout page where they complete their purchase. Simpler will then call your Platform Interface APIs to price the cart, resolve shipping, and submit the order.
Cart Request Schema
The Cart Request is a JSON-serializable object with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
merchant_id | string | Yes | Your App ID. |
currency | string | Yes | ISO 4217 currency code (e.g. EUR, USD, GBP). Any valid ISO 4217 code is accepted. |
locale | string | Yes | IETF language tag per RFC 5646 (e.g. en, el, fr). Any valid tag is accepted. |
items | array | Yes | One or more line items (must not be empty). See below. |
coupon | string | No | A coupon code to pre-apply in the Simpler checkout form. |
metadata | object | No | String key-value pairs passed through to your backend on every subsequent Platform Interface call for this session. Intended for read-only attribution or tracking (A/B test buckets, internal references). Do not use it as a mutable state store — see Cart Metadata. |
items[]
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Product identifier as used in your Platform Interface product catalog. |
quantity | integer | Yes | Number of units. Integer, minimum 1. |
attributes | object | No | Key-value pairs identifying a specific product variant (e.g. {"color": "blue", "size": "L"}). For products with variants, keys must match a catalog variant definition in your Platform Interface. Omit for simple products with no variants. |
The client-side flow uses the shorter names qty and attrs for the same fields. This difference is intentional — each transport has its own naming.
Sending the Request
POST the cart data as application/x-www-form-urlencoded to:
https://checkout.simpler.so/v1/carts
JSON request bodies are not supported. Because the payload is form-encoded, nested fields (items, attributes, metadata) are serialised using bracket notation — this is what http_build_query() produces. For example, a single item serialises on the wire as:
merchant_id=YOUR_APP_ID¤cy=EUR&locale=en&items%5B0%5D%5Bid%5D=PRODUCT_ID&items%5B0%5D%5Bquantity%5D=1&items%5B0%5D%5Battributes%5D%5Bcolor%5D=blue
On success, the server responds with a 303 See Other status and a Location header pointing to the Simpler Checkout page. Your server should forward this redirect to the user's browser:
HTTP/1.1 303 See Other
Location: https://checkout.simpler.so/carts/<uuid>
Content-Length: 0
The Location URL is reusable and does not expire. Note that each request creates a new checkout session — re-POSTing the same cart produces a duplicate session, and there is no idempotency key, so avoid blindly retrying this call on the redirect path.
PHP + cURL Example
$url = 'https://checkout.simpler.so/v1/carts';
$data = [
'merchant_id' => 'YOUR_APP_ID',
'currency' => 'EUR',
'locale' => 'en',
'items' => [
[
'id' => 'PRODUCT_ID',
'quantity' => 1,
'attributes' => [
'color' => 'blue',
'size' => 'L'
]
]
],
'coupon' => 'WELCOME10',
'metadata' => [
'source' => 'cart_page'
]
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_FOLLOWLOCATION => false,
]);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
if ($info['http_code'] == 303) {
preg_match('/^Location:\s+(.*)$/mi', $response, $matches);
$location = trim($matches[1]);
curl_close($ch);
header("Location: $location", true, 303);
exit;
}
// The request did not result in a redirect — something went wrong.
$headerSize = $info['header_size'];
$body = substr($response, $headerSize);
curl_close($ch);
error_log("Cart request failed with status {$info['http_code']}: $body");
The example sets CURLOPT_FOLLOWLOCATION => false on purpose: you want to capture the Location header and forward the redirect to the shopper's browser, not follow it server-side. Re-emitting it with header("Location: $location", true, 303) preserves the 303 status so the browser performs the redirect.
curl Example
curl -i -X POST https://checkout.simpler.so/v1/carts \
--data-urlencode 'merchant_id=YOUR_APP_ID' \
--data-urlencode 'currency=EUR' \
--data-urlencode 'locale=en' \
--data-urlencode 'items[0][id]=PRODUCT_ID' \
--data-urlencode 'items[0][quantity]=1' \
--data-urlencode 'items[0][attributes][color]=blue' \
--data-urlencode 'items[0][attributes][size]=L' \
--data-urlencode 'coupon=WELCOME10' \
--data-urlencode 'metadata[source]=cart_page'
The -i flag includes the response headers in the output, so you can see the 303 status and the Location header.
Error Responses
All error responses return a JSON body with a message field describing the issue.
| Status Code | Meaning |
|---|---|
400 | Invalid request body. |
404 | Invalid App ID / Store not found. |
500 | Internal server error. |