Record orders paid outside Kreezalid
When your app or headless storefront collects the payment itself, Kreezalid does not see it
happen. Once your payment provider confirms the preauthorization (the amount is reserved
on the customer’s card), call POST /api/v1/checkout/preauth-success from your server. Kreezalid
then does what its own checkout would have done: it closes the cart, turns each of its orders
into a new order for its vendor, and notifies the customer and the vendors.
Before you start
Section titled “Before you start”- The cart was built with the Front API and you know its numeric
id:POST /front_api/v1/cartsreturns it incart.id, andGET /front_api/v1/carts/{url_key}incart.id. - The marketplace has an active payment gateway. Without one the call answers
400withNo active PSP config found. See Payment gateways. - The call uses Admin API credentials, so it is made from your server, never from the app or the browser.
The flow
Section titled “The flow”-
Freeze the amount. Read the cart (
GET /front_api/v1/carts/{url_key}) once shipping methods and the address are set. Chargetotal_after_discount, in the cart’scurrency. -
Preauthorize that amount with your payment provider. Keep its transaction or authorization id.
-
Collect the terms acceptance. Record when the customer accepted the marketplace’s terms of sale, in UTC.
-
Record the payment. From your server:
Terminal window curl -X POST https://acme.mykreezalid.com/api/v1/checkout/preauth-success \-u "$KREEZALID_API_KEY:$KREEZALID_API_SECRET" \-H "Content-Type: application/json" \-d '{"checkout_entity_type": "cart","checkout_entity_id": 7781,"transaction_id": "pi_3QabcXYZ2026","tos_acceptance_at": "2026-10-13 14:30:00","customer_checkout_message": "Please ring twice."}'server/record-payment.mjs export async function recordPreauth({ cartId, transactionId, acceptedAt, message }) {const auth = Buffer.from(`${process.env.KREEZALID_API_KEY}:${process.env.KREEZALID_API_SECRET}`).toString('base64');const res = await fetch('https://acme.mykreezalid.com/api/v1/checkout/preauth-success', {method: 'POST',headers: { Authorization: `Basic ${auth}`, 'Content-Type': 'application/json' },body: JSON.stringify({checkout_entity_type: 'cart',checkout_entity_id: cartId,transaction_id: transactionId,// "YYYY-MM-DD HH:MM:SS", UTCtos_acceptance_at: acceptedAt.toISOString().slice(0, 19).replace('T', ' '),customer_checkout_message: message ?? undefined,}),});const body = await res.json();if (body.status !== 'success') {throw Object.assign(new Error(body.message), { status: res.status, errors: body.errors });}return body.cart;}record-payment.php <?php$payload = ['checkout_entity_type' => 'cart','checkout_entity_id' => 7781,'transaction_id' => 'pi_3QabcXYZ2026','tos_acceptance_at' => gmdate('Y-m-d H:i:s', $acceptedAtTimestamp),'customer_checkout_message' => 'Please ring twice.',];$ch = curl_init('https://acme.mykreezalid.com/api/v1/checkout/preauth-success');curl_setopt_array($ch, [CURLOPT_USERPWD => getenv('KREEZALID_API_KEY') . ':' . getenv('KREEZALID_API_SECRET'),CURLOPT_POST => true,CURLOPT_POSTFIELDS => json_encode($payload),CURLOPT_HTTPHEADER => ['Content-Type: application/json'],CURLOPT_RETURNTRANSFER => true,]);$body = json_decode(curl_exec($ch), true);if (($body['status'] ?? null) !== 'success') {throw new RuntimeException($body['message'] ?? 'Preauth not recorded');}record_payment.py from datetime import datetime, timezoneimport os, requestsres = requests.post("https://acme.mykreezalid.com/api/v1/checkout/preauth-success",auth=(os.environ["KREEZALID_API_KEY"], os.environ["KREEZALID_API_SECRET"]),json={"checkout_entity_type": "cart","checkout_entity_id": 7781,"transaction_id": "pi_3QabcXYZ2026","tos_acceptance_at": datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S"),"customer_checkout_message": "Please ring twice.",},)body = res.json()if body.get("status") != "success":raise RuntimeError(body.get("message"), body.get("errors")) -
Confirm to the customer with the order numbers from the response, or from the
order.status.newwebhook.
Request fields
Section titled “Request fields”| Field | Required | Description |
|---|---|---|
checkout_entity_type |
Yes | cart. |
checkout_entity_id |
Yes | The numeric id of the cart, not its url_key. |
transaction_id |
Yes | Your payment provider’s id for the authorization. It is stored on every order of the cart. |
tos_acceptance_at |
Yes | When the customer accepted the terms, formatted exactly YYYY-MM-DD HH:MM:SS. |
customer_checkout_message |
No | A note from the customer, stored on the cart and on each of its orders. Must be a string. |
What Kreezalid does
Section titled “What Kreezalid does”For each order of the cart that is still a draft:
- the preauthorization is recorded on the order, with your
transaction_id; - the order leaves
draft. It becomesnew, waiting for the vendor to accept it, unless its payment is configured to be captured at checkout, in which case it becomescompleted. A free order always becomesnew; - an
order.status.newevent is emitted in both cases, and the customer and the vendor receive the marketplace’s new-order emails; - for bookings, the booked dates are blocked in the calendar.
The cart is then closed. The response returns it with its orders:
{ "code": 200, "status": "success", "message": "Preauth saved and cart updated successfully", "cart": { "id": 7781, "url_key": "q8Zt3k…", "cart_status": "closed", "orders": [ { "id": 90311, "order_number": "R-1042", "status": "new", "supplier_id": 4242, "…": "…" }, { "id": 90312, "order_number": "R-1043", "status": "new", "supplier_id": 4318, "…": "…" } ] }}Errors
Section titled “Errors”| Status | message |
Cause |
|---|---|---|
400 |
Invalid request params, with an errors list |
A field is missing or malformed, or the cart id does not exist. Each entry names the field. |
400 |
No active PSP config found |
The marketplace has no active payment gateway. |
403 |
This cart cannot be updated because its status is: closed |
The cart was already recorded. Do not retry: read the orders instead. |
The call is not idempotent by itself: a timeout does not tell you whether the cart was closed.
Before retrying after a network error, read the cart again (GET /front_api/v1/carts/{url_key}
answers 404 once it is closed) or look for its orders with
GET /api/v1/orders?cart_id=7781.
After the vendor answers
Section titled “After the vendor answers”Kreezalid stores your transaction_id but does not capture or release it with your provider.
Drive that from the order webhooks:
| Event | Do |
|---|---|
order.status.accepted |
Capture the order’s amount with your provider. |
order.status.declined, order.status.canceled |
Release or refund the authorization for that order. |
A cart with several vendors produces several orders that can be accepted or declined
independently: capture order by order, using each order’s amount.