Orders and Payment Binding
An order is the purchase of the quote option the buyer confirmed. Premium collection, policy, refund and document processes are tracked on the order. The order's status axes and overall status: Concepts and Statuses › Order statuses.
Authentication, Idempotency-Key and the error envelope: Flows.
Creating an order: POST /api/v1/insurance/orders
Idempotency-Key is required. Creating an order sends no request to the provider and moves no money; it records the option and the buyer's consent.
| Field | Type | Required | Description |
|---|---|---|---|
quoteId | string | Yes | ID of the option the buyer confirmed (insq_…). |
merchantOrderId | string | No | Your own order number, up to 128 characters. If omitted, the value from the quote is used. If the quote also has a value, both must be the same. |
consent.termsVersion | string | Yes | The option's terms.version value, exactly as received. |
consent.termsSha256 | string | Yes | The option's terms.sha256 value, exactly as received. |
consent.acceptedAt | string | Yes | The moment the buyer gave consent. ISO 8601 with a time zone; cannot be in the future. |
Consent
consentrecords that the buyer accepts the option's terms and consents to their details being shared with the provider for the insurance.- The terms values must equal the option's. The terms of another option or of an old quote do not count as accepted: the request returns
422 insurance.validation.invalid(field: consent.termsSha256,reason: terms_mismatch). acceptedAtcan be at most a few minutes ahead of server time; a later time is rejected.
Rules
- The option must be valid: an expired option returns
409 insurance.quote.expired. - Only one order can be created from an option. A second order returns
409 insurance.order.state_conflict(reason: quote_already_ordered). - Only quotes you got from the API can be turned into orders.
{
"quoteId": "insq_<32 hex characters>",
"merchantOrderId": "order-1001",
"consent": {
"termsVersion": "<the option's terms.version value>",
"termsSha256": "<the option's terms.sha256 value>",
"acceptedAt": "2025-12-20T10:15:00+03:00"
}
}source lunixi-sign.sh
BODY='<order request, compact JSON>'
lunixi_sign POST "/api/v1/insurance/orders" "$BODY"
curl -X POST "https://api-gateway.lunixi.com/api/v1/insurance/orders" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: insurance-order-1001" \
-H "X-Key-Id: $LUNIXI_KEY_ID" \
-H "X-Date: $DATE" \
-H "X-Nonce: $NONCE" \
-H "Digest: $DIGEST" \
-H "X-Signature: $SIGNATURE" \
--data-raw "$BODY"A successful request returns 201 and the order object in data. In a new order paymentMode is NO_PAYMENT, collection is NOT_STARTED and policy is NOT_REQUESTED.
The next step is collecting the premium: Premium and Payment.
Order object
| Field | Type | Description |
|---|---|---|
orderId | string | Order ID (inso_…). In a Lunixi payment, this is the premium item's id. |
quoteId | string | The option the order was created from. |
insuranceType | string | Insurance type. |
surface | string | Where the order was created: MERCHANT_API or CHECKOUT_FORM. |
status | string | Overall status; derived from the four axes. See Concepts › Overall status. |
collectionStatus | string | Collection axis. |
policyStatus | string | Policy axis. |
refundStatus | string | Refund axis. |
documentStatus | string | Document axis. |
paymentMode | string | How the premium was paid: NO_PAYMENT (not bound yet), LUNIXI_PAYMENT (bound to a Lunixi payment) or EXTERNAL_PAYMENT (paid outside Lunixi). |
premiumCollectedBy | string | Who collects the premium: MERCHANT when you collect it, PROVIDER when your provider collects it (Provider Collection). |
premiumAmount | integer | Premium, in minor units. |
currency | string | Currency, ISO 4217. |
paymentIntentId | string or null | ID of the bound Lunixi payment. |
merchantOrderId | string or null | Your own order number. |
provider | object | The provider: code, name, logoUrl and connectionId, the ID of your connection the policy is issued through. You reconcile with your provider using this information. |
basketItemIds | string[] | The insured basket items. |
policies | array | Policies per item; fields below. |
errorCode | string or null | Lunixi code of the last error on the collection or policy side, e.g. insurance.premium.not_captured, insurance.payment.declined, insurance.policy.issue_failed. |
refundErrorCode | string or null | Lunixi code of the last refund attempt, e.g. insurance.refund.rejected. |
refundedAt | string or null | The moment the refund was finalized. |
nextAction | object or null | The step the buyer must take while the provider collects the premium; null when no step is pending. See Provider Collection › Buyer action. |
createdAt | string | Creation time, ISO 8601. |
updatedAt | string | Last change time, ISO 8601. |
policies[]:
| Field | Type | Description |
|---|---|---|
policyId | string | Policy ID (insp_…). You get the document with this ID: Policy Document. |
basketItemId | string | The basket item the policy insures. |
policyNumber | string or null | The number on the policy document. null until the policy is issued. |
policyStatus | string | Status of this policy; the values are the same as the policy axis. |
documentStatus | string | Document status of this policy. |
Policies are created per item after the premium is collected. In a new order policies is an empty array.
Listing: GET /api/v1/insurance/orders
Returns your orders newest first, in cursor-based pages. Orders created in the Checkout Form are also listed (surface: CHECKOUT_FORM).
| Query parameter | Type | Description |
|---|---|---|
limit | integer | Records per page, 1-100. Default 25. |
cursor | string | pageInfo.nextCursor from the previous response. Not sent for the first page. |
paymentIntentId | string (UUID) | Only orders bound to this Lunixi payment. |
merchantOrderId | string | Only orders carrying this order number. |
In the response, the orders are in the data.items array; the pagination information is in pageInfo. A malformed cursor returns 422 insurance.validation.invalid (field: cursor).
source lunixi-sign.sh
REQ_PATH="/api/v1/insurance/orders?limit=25&merchantOrderId=order-1001"
lunixi_sign GET "$REQ_PATH"
curl "https://api-gateway.lunixi.com$REQ_PATH" \
-H "X-Key-Id: $LUNIXI_KEY_ID" \
-H "X-Date: $DATE" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIGNATURE"Retrieving: GET /api/v1/insurance/orders/{orderId}
Returns a single order; data is the order object. You track the order's policy and document status with this endpoint. If the ID is not found or belongs to another account, 404 insurance.order.not_found is returned.
Binding to a payment: POST /api/v1/insurance/orders/{orderId}/bind-payment
Binds the order to the Lunixi payment that carries the premium in its basket. Idempotency-Key is optional.
| Field | Type | Required | Description |
|---|---|---|---|
paymentIntentId | string (UUID) | Yes | Lunixi payment ID. |
Binding is checked in this order:
- The order must have been created through the API, must not be bound to another payment and must not have been paid outside Lunixi. An order whose collection has started cannot be bound.
- The order's option must be valid (
409 insurance.quote.expired). - The payment must have been created with insured items (
412 insurance.order.payment_not_recorded). - The payment's currency must equal the order's.
- The payment's insured items must be the same as the quoted items (
409 insurance.quote.stale). - The payment's basket must contain the premium item exactly once:
idis the order ID,itemTypeisVIRTUAL,priceis the premium (409 insurance.order.premium_item_mismatch). If the provider collects the premium (premiumCollectedBy: PROVIDER), the basket has no premium item: Provider Collection.
Binding the same payment again returns the order as it is. In a successful response paymentMode becomes LUNIXI_PAYMENT. The premium item and the payment request: Premium and Payment › Lunixi payment.
details.reason in an insurance.order.premium_item_mismatch response:
reason | Meaning |
|---|---|
missing | The basket has no premium item with the order ID, or has more than one. |
amount | The premium item's price differs from the premium. |
item_type | The premium item's itemType is not VIRTUAL. |
not_allowed | The provider collects the premium; the basket must not contain a premium item with the order ID. |
Reporting a premium collected outside Lunixi: POST /api/v1/insurance/orders/{orderId}/confirm-payment
If you collected the premium outside Lunixi, you report the payment. Lunixi forwards the notification to your provider through your connection. Idempotency-Key is required.
| Field | Type | Required | Description |
|---|---|---|---|
payment.externalPaymentId | string | Yes | The payment ID on your side; 1-128 printable ASCII characters. |
payment.paidAmount | integer | Yes | Amount paid, in minor units. Must equal the order's premium. |
payment.paidCurrency | string | Yes | Currency, ISO 4217. Must equal the order's currency. |
payment.paidAt | string | Yes | The moment of payment. ISO 8601 with a time zone; cannot be in the future. |
payment.paymentProvider | string | No | The institution you took the payment with; up to 64 characters, informational. |
- If the order is bound to a Lunixi payment, the notification is not accepted (
409 insurance.order.state_conflict,reason: lunixi_payment). - The order's option must be valid (
409 insurance.quote.expired). - If the amount or the currency does not match,
422 insurance.validation.invalidis returned (reason: premium_amount_mismatchorpremium_currency_mismatch). - In a successful response
paymentModebecomesEXTERNAL_PAYMENTand collection becomesCOLLECTED. Repeating with the sameexternalPaymentIdreturns the order as it is; a different ID returns409(reason: external_payment).
{
"payment": {
"externalPaymentId": "pay-1001",
"paidAmount": 3980,
"paidCurrency": "TRY",
"paidAt": "2025-12-20T10:16:00+03:00"
}
}The amount is an example; paidAmount is the order's premiumAmount value.
Requesting the policy: POST /api/v1/insurance/orders/{orderId}/issue-policy
Requests the policy after a premium collected outside Lunixi. No body is sent. Idempotency-Key is required.
- If the premium has not been collected,
412 insurance.premium.not_capturedis returned. - The response is the state in which the request was recorded:
policyStatusPENDING. The policy is issued in the background; track the result through the order endpoint. - If the policy was already requested or has a result, the order is returned as it is.
- If the premium refund has started, the policy cannot be requested (
409,reason: refund_started).
You do not need this endpoint for an order bound to a Lunixi payment: the policy is requested automatically when the payment is captured.
State conflict
409 insurance.order.state_conflict means the order's state does not allow the operation. The reason is in details.reason; retrying the same request does not change the result.
reason | Meaning |
|---|---|
quote_already_ordered | An order was already created from this option. |
not_api_order | The order was created in the Checkout Form; it cannot be bound to a payment through the API. |
already_bound | The order is bound to another payment. |
quote_bound_to_other_payment | The quote was taken with another payment. |
external_payment | The order's premium was reported as paid outside Lunixi. |
lunixi_payment | The order is bound to a Lunixi payment; a payment outside Lunixi cannot be reported. |
collection_started | Collection of the premium has started. |
collected_by_provider | The provider collects the premium; this operation is not possible. |
refund_started | The premium refund has started or completed. |
Reasons specific to the cancellation, refund and document endpoints are on their own pages.
Errors
| HTTP | code | What to do |
|---|---|---|
400 | VALIDATION_ERROR | Fix the format; this error is also returned when Idempotency-Key is missing. |
422 | insurance.validation.invalid | Fix the fields in details.errors[]. |
404 | insurance.quote.not_found | The option was not found. |
404 | insurance.order.not_found | The order was not found. |
409 | insurance.quote.expired | The option has expired; get a new quote. |
409 | insurance.quote.stale | The payment's basket differs from the quote; get a new quote. |
409 | insurance.order.state_conflict | See details.reason; table above. |
409 | insurance.order.premium_item_mismatch | Fix the premium item; see details.reason. |
412 | insurance.order.payment_not_recorded | Create the payment with insured items. |
412 | insurance.premium.not_captured | The policy cannot be requested before the premium is collected. |
409 | insurance.idempotency.conflict | The same Idempotency-Key was used with a different request. |
Common codes: Flows › Responses and errors.
Related
- Premium and Payment: the premium item and collection outside Lunixi
- Policy Document: the document of an issued policy
- Cancellation and Premium Refund: policy cancellation and premium refund