Ordeliya Docs

Orders API

Create, manage, and track the full order lifecycle — from placement to completion — through the Ordeliya API.

Overview

The Orders API is the core of the Ordeliya platform. Every order is scoped to the authenticated store. Tenant users (Owner, Admin, Manager, Staff) can view and manage all orders. Customers can only view their own orders through the storefront endpoints.

All monetary values are in minor units (integer cents/ore). 24900 = 249.00 DKK.


GET /orders

List orders with optional filters. Returns a paginated list sorted by createdAt descending.

Auth: Bearer Token or API Key (read:orders)

curl "https://api.ordeliya.com/orders?status=RECEIVED&limit=10" \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Query Parameters:

ParameterTypeDefaultDescription
statusstringFilter: RECEIVED, CONFIRMED, PREPARING, READY, ON_THE_WAY, COMPLETED, CANCELLED
fulfillmentTypestringFilter: DELIVERY, PICKUP, CURBSIDE
paymentStatusstringFilter: PENDING, COMPLETED, FAILED, REFUNDED
sourcestringFilter: WEB, APP, POS, PHONE
customerIdstringFilter by customer ID
fromISO 8601Orders created after this date
toISO 8601Orders created before this date
pageinteger1Page number
limitinteger20Items per page (max: 100)
sortstringcreatedAtSort field
orderstringdescSort direction: asc or desc

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "ord_v7k3m9n2",
      "orderId": "2026-0147",
      "status": "RECEIVED",
      "fulfillmentType": "DELIVERY",
      "source": "WEB",
      "paymentStatus": "COMPLETED",
      "paymentProvider": "STRIPE",
      "customer": {
        "id": "cust_n3k7m2",
        "name": "Maria Nielsen",
        "phone": "+4520123456",
        "email": "maria@example.dk"
      },
      "items": [
        {
          "id": "oi_x1y2z3",
          "productId": "prod_8kx2m4n7",
          "productName": "Margherita Pizza",
          "variantId": "var_lg01",
          "variantName": "Large",
          "quantity": 2,
          "unitPriceMinor": 11900,
          "totalMinor": 23800,
          "options": [
            {
              "optionGroupName": "Extras",
              "choiceName": "Extra Mozzarella",
              "priceMinor": 1500
            }
          ],
          "notes": "Extra crispy"
        },
        {
          "id": "oi_a4b5c6",
          "productId": "prod_3jn8v5q2",
          "productName": "Garlic Bread",
          "variantId": null,
          "variantName": null,
          "quantity": 1,
          "unitPriceMinor": 3900,
          "totalMinor": 3900,
          "options": [],
          "notes": null
        }
      ],
      "subtotalMinor": 30700,
      "taxMinor": 7675,
      "deliveryFeeMinor": 2900,
      "discountMinor": 0,
      "totalMinor": 33600,
      "currency": "DKK",
      "notes": "3rd floor, code 4521",
      "deliveryAddress": {
        "street": "Vesterbrogade 42",
        "zipcode": "1620",
        "city": "Copenhagen V",
        "country": "DK"
      },
      "scheduledAt": null,
      "createdAt": "2026-03-15T18:42:11.000Z",
      "updatedAt": "2026-03-15T18:42:11.000Z"
    }
  ],
  "meta": {
    "total": 1247,
    "page": 1,
    "limit": 10,
    "totalPages": 125,
    "requestId": "req_k8j7h6g5f4d3"
  }
}

GET /orders/:id

Get full order details including items, customer, delivery address, payment info, and status timeline.

Auth: Bearer Token or API Key (read:orders)

curl https://api.ordeliya.com/orders/ord_v7k3m9n2 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

Returns the same order object as the list endpoint, plus the timeline array:

{
  "success": true,
  "data": {
    "id": "ord_v7k3m9n2",
    "orderId": "2026-0147",
    "status": "PREPARING",
    "timeline": [
      { "status": "RECEIVED", "timestamp": "2026-03-15T18:42:11.000Z", "actor": "system" },
      { "status": "CONFIRMED", "timestamp": "2026-03-15T18:43:22.000Z", "actor": "usr_4k7m2n8v" },
      { "status": "PREPARING", "timestamp": "2026-03-15T18:45:01.000Z", "actor": "usr_s7t8a9f0" }
    ]
  }
}

Error 404 Not Found

{
  "success": false,
  "error": {
    "statusCode": 404,
    "message": "Order not found"
  }
}

The order might exist but belong to a different store. Orders are strictly scoped — you can only access orders from your authenticated store.


POST /orders

Create a new order. Idempotency key is strongly recommended to prevent duplicate orders.

Auth: Bearer Token or API Key (write:orders)

curl -X POST https://api.ordeliya.com/orders \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: pos_device01_txn_20260315_001" \
  -d '{
    "fulfillmentType": "DELIVERY",
    "source": "POS",
    "customerId": "cust_n3k7m2",
    "items": [
      {
        "productId": "prod_8kx2m4n7",
        "variantId": "var_lg01",
        "quantity": 1,
        "options": [
          { "optionChoiceId": "opt_c7h8e9e0" }
        ],
        "notes": "Well done"
      },
      {
        "productId": "prod_3jn8v5q2",
        "quantity": 2
      }
    ],
    "deliveryAddress": {
      "street": "Nørrebrogade 15",
      "zipcode": "2200",
      "city": "Copenhagen N",
      "country": "DK"
    },
    "notes": "Please ring doorbell twice",
    "scheduledAt": "2026-03-15T20:00:00.000Z"
  }'

Request Body:

FieldTypeRequiredDescription
fulfillmentTypestringyesDELIVERY, PICKUP, or CURBSIDE
sourcestringyesWEB, APP, POS, or PHONE
customerIdstringnoLink to existing customer
customerNamestringnoGuest name (if no customerId)
customerPhonestringnoGuest phone (if no customerId)
customerEmailstringnoGuest email (if no customerId)
itemsarrayyesAt least one item required
items[].productIdstringyesProduct ID
items[].variantIdstringnoVariant ID (if product has variants)
items[].quantityintegeryesMin: 1
items[].optionsarraynoOption choices for modifiers
items[].notesstringnoItem-level special instructions
deliveryAddressobjectconditionalRequired when fulfillmentType is DELIVERY
notesstringnoOrder-level notes
scheduledAtISO 8601noFuture time for scheduled orders (null = ASAP)

Response 201 Created

{
  "success": true,
  "data": {
    "id": "ord_q2w3e4r5",
    "orderId": "2026-0148",
    "status": "RECEIVED",
    "fulfillmentType": "DELIVERY",
    "source": "POS",
    "customer": {
      "id": "cust_n3k7m2",
      "name": "Maria Nielsen",
      "phone": "+4520123456",
      "email": "maria@example.dk"
    },
    "items": [
      {
        "id": "oi_d7e8f9",
        "productId": "prod_8kx2m4n7",
        "productName": "Margherita Pizza",
        "variantId": "var_lg01",
        "variantName": "Large",
        "quantity": 1,
        "unitPriceMinor": 11900,
        "totalMinor": 13400,
        "options": [
          {
            "optionGroupName": "Extras",
            "choiceName": "Extra Mozzarella",
            "priceMinor": 1500
          }
        ],
        "notes": "Well done"
      },
      {
        "id": "oi_g0h1i2",
        "productId": "prod_3jn8v5q2",
        "productName": "Garlic Bread",
        "variantId": null,
        "variantName": null,
        "quantity": 2,
        "unitPriceMinor": 3900,
        "totalMinor": 7800,
        "options": [],
        "notes": null
      }
    ],
    "subtotalMinor": 21200,
    "taxMinor": 5300,
    "deliveryFeeMinor": 2900,
    "discountMinor": 0,
    "totalMinor": 24100,
    "currency": "DKK",
    "paymentStatus": "PENDING",
    "deliveryAddress": {
      "street": "Nørrebrogade 15",
      "zipcode": "2200",
      "city": "Copenhagen N",
      "country": "DK"
    },
    "notes": "Please ring doorbell twice",
    "scheduledAt": "2026-03-15T20:00:00.000Z",
    "createdAt": "2026-03-15T19:15:44.000Z"
  }
}

Error 422 Unprocessable Entity

{
  "success": false,
  "error": {
    "statusCode": 422,
    "message": "Validation failed",
    "errors": [
      { "field": "items[0].productId", "message": "Product not found or inactive" },
      { "field": "deliveryAddress", "message": "Required for DELIVERY fulfillment type" }
    ]
  }
}

PATCH /orders/:id/status

Advance the order through the status workflow. Only valid transitions are allowed.

Auth: Bearer Token or API Key (write:orders)

curl -X PATCH https://api.ordeliya.com/orders/ord_v7k3m9n2/status \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "status": "CONFIRMED",
    "note": "Order confirmed by kitchen"
  }'

Request Body:

FieldTypeRequiredDescription
statusstringyesTarget status (see flow below)
notestringnoInternal note for the timeline

Response 200 OK

{
  "success": true,
  "data": {
    "id": "ord_v7k3m9n2",
    "orderId": "2026-0147",
    "status": "CONFIRMED",
    "previousStatus": "RECEIVED",
    "updatedAt": "2026-03-15T18:43:22.000Z"
  }
}

Error 400 Bad Request — Invalid Transition

{
  "success": false,
  "error": {
    "statusCode": 400,
    "message": "Invalid status transition from COMPLETED to PREPARING"
  }
}

DELETE /orders/:id

Soft-delete an order. The order is marked as archived but retained for reporting. Only RECEIVED or CANCELLED orders can be deleted.

Auth: Bearer Token (write:orders, Owner or Admin role)

curl -X DELETE https://api.ordeliya.com/orders/ord_v7k3m9n2 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 204 No Content

No response body. The order is archived.

Error 400 Bad Request

{
  "success": false,
  "error": {
    "statusCode": 400,
    "message": "Cannot delete order with status CONFIRMED. Cancel the order first."
  }
}

GET /orders/:id/timeline

Get the full status history of an order.

Auth: Bearer Token or API Key (read:orders)

curl https://api.ordeliya.com/orders/ord_v7k3m9n2/timeline \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": [
    {
      "status": "RECEIVED",
      "timestamp": "2026-03-15T18:42:11.000Z",
      "actor": "system",
      "note": "Order placed via storefront"
    },
    {
      "status": "CONFIRMED",
      "timestamp": "2026-03-15T18:43:22.000Z",
      "actor": "usr_4k7m2n8v",
      "actorName": "Anders Jensen",
      "note": "Order confirmed by kitchen"
    },
    {
      "status": "PREPARING",
      "timestamp": "2026-03-15T18:45:01.000Z",
      "actor": "usr_s7t8a9f0",
      "actorName": "Sofia Larsen",
      "note": null
    },
    {
      "status": "READY",
      "timestamp": "2026-03-15T19:05:33.000Z",
      "actor": "usr_s7t8a9f0",
      "actorName": "Sofia Larsen",
      "note": "Food packed and ready"
    },
    {
      "status": "ON_THE_WAY",
      "timestamp": "2026-03-15T19:08:45.000Z",
      "actor": "system",
      "note": "Driver assigned: Jakob"
    },
    {
      "status": "COMPLETED",
      "timestamp": "2026-03-15T19:28:12.000Z",
      "actor": "system",
      "note": "Delivered successfully"
    }
  ]
}

GET /orders/stats

Get aggregate order statistics for the current store. Useful for dashboard widgets.

Auth: Bearer Token or API Key (read:orders)

curl https://api.ordeliya.com/orders/stats \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": {
    "totalOrders": 1247,
    "todayOrders": 23,
    "pendingOrders": 5,
    "totalRevenueMinor": 1456700,
    "todayRevenueMinor": 42300,
    "averageOrderMinor": 11680,
    "statusBreakdown": {
      "RECEIVED": 3,
      "CONFIRMED": 2,
      "PREPARING": 4,
      "READY": 1,
      "ON_THE_WAY": 2,
      "COMPLETED": 1218,
      "CANCELLED": 17,
      "REFUNDED": 0
    }
  }
}

Refunds

POST /refunds

Create a refund request for an order. Refunds go through an approval workflow: PENDINGAPPROVEDPROCESSED (or REJECTED).

Auth: Bearer Token (write:orders, Manager role or above)

curl -X POST https://api.ordeliya.com/refunds \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "ord_v7k3m9n2",
    "type": "PARTIAL",
    "reason": "QUALITY_ISSUE",
    "reasonText": "Customer reported cold pizza",
    "refundAmountMinor": 8900,
    "items": [
      {
        "productName": "Margherita Pizza",
        "qty": 1,
        "refundAmountMinor": 8900
      }
    ]
  }'
FieldTypeRequiredDescription
orderIdstringyesThe order to refund
typestringyesFULL or PARTIAL
reasonstringyesCUSTOMER_REQUEST, QUALITY_ISSUE, DUPLICATE_ORDER, OTHER
reasonTextstringnoFree-text explanation
refundAmountMinorintegeryesAmount to refund in minor units
itemsarraynoLine-item breakdown for partial refunds

Response 201 Created

{
  "success": true,
  "data": {
    "id": "ref_m3n4o5p6",
    "orderId": "ord_v7k3m9n2",
    "type": "PARTIAL",
    "reason": "QUALITY_ISSUE",
    "reasonText": "Customer reported cold pizza",
    "refundAmountMinor": 8900,
    "currency": "DKK",
    "status": "PENDING",
    "items": [
      {
        "productName": "Margherita Pizza",
        "qty": 1,
        "refundAmountMinor": 8900
      }
    ],
    "createdAt": "2026-03-15T20:15:00.000Z"
  }
}

GET /refunds

List all refunds for the current store.

Auth: Bearer Token or API Key (read:orders)

curl "https://api.ordeliya.com/refunds?status=PENDING&page=1&limit=20" \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."
ParameterTypeDefaultDescription
statusstringFilter by status: PENDING, APPROVED, REJECTED, PROCESSED
orderIdstringFilter by order ID
pageinteger1Page number
limitinteger20Items per page

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "ref_m3n4o5p6",
      "orderId": "ord_v7k3m9n2",
      "type": "PARTIAL",
      "reason": "QUALITY_ISSUE",
      "refundAmountMinor": 8900,
      "currency": "DKK",
      "status": "PENDING",
      "createdAt": "2026-03-15T20:15:00.000Z"
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 20,
    "totalPages": 1
  }
}

GET /refunds/:id

Get a single refund by ID.

Auth: Bearer Token or API Key (read:orders)

curl https://api.ordeliya.com/refunds/ref_m3n4o5p6 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

PATCH /refunds/:id/approve

Approve a pending refund. Only PENDING refunds can be approved.

Auth: Bearer Token (Admin or Owner role)

curl -X PATCH https://api.ordeliya.com/refunds/ref_m3n4o5p6/approve \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": {
    "id": "ref_m3n4o5p6",
    "status": "APPROVED",
    "approvedAt": "2026-03-15T20:30:00.000Z"
  }
}

PATCH /refunds/:id/reject

Reject a pending refund.

Auth: Bearer Token (Admin or Owner role)

curl -X PATCH https://api.ordeliya.com/refunds/ref_m3n4o5p6/reject \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

PATCH /refunds/:id/process

Mark an approved refund as processed (money returned to customer).

Auth: Bearer Token (Admin or Owner role)

curl -X PATCH https://api.ordeliya.com/refunds/ref_m3n4o5p6/process \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": {
    "id": "ref_m3n4o5p6",
    "status": "PROCESSED",
    "processedAt": "2026-03-15T21:00:00.000Z"
  }
}

Order Status Flow

Orders follow a strict state machine. Only valid transitions are allowed.

                              ┌─────────────┐
                              │  CANCELLED   │
                              └──────▲───────┘

┌──────────┐   ┌───────────┐   ┌────┴──────┐   ┌─────────┐   ┌───────────┐
│ RECEIVED │──▸│ CONFIRMED │──▸│ PREPARING │──▸│  READY  │──▸│ COMPLETED │
└──────────┘   └───────────┘   └───────────┘   └────┬────┘   └───────────┘

                                               ┌─────▼──────┐
                                               │ ON_THE_WAY │──▸ COMPLETED
                                               └────────────┘

Valid transitions:

FromTo
RECEIVEDCONFIRMED, CANCELLED
CONFIRMEDPREPARING, CANCELLED
PREPARINGREADY, CANCELLED
READYON_THE_WAY, COMPLETED, CANCELLED
ON_THE_WAYCOMPLETED, CANCELLED
COMPLETEDREFUNDED
CANCELLED— (terminal state)
REFUNDED— (terminal state)

Order Sources

SourceDescriptionTypical Use
WEBCustomer storefront orderOnline ordering
APPMobile app orderNative app
POSPoint of sale terminalIn-store
PHONEManual phone orderCall-in orders

Fulfillment Types

TypeDescriptionDelivery Address
DELIVERYDelivered to customerRequired
PICKUPCustomer picks up at storeNot needed
CURBSIDECustomer picks up at curbNot needed

Payment Status

StatusDescription
PENDINGAwaiting payment (cash orders, pay-at-pickup)
COMPLETEDPayment received and confirmed
FAILEDPayment attempt failed
REFUNDEDFull refund issued

Money Format

All prices are integers in minor units:

ValueCurrencyDisplay
8900DKK89,00 kr
1999EUR19,99 EUR
24900TRY249,00 TL
0DKK0,00 kr (free)

Fields ending in Minor contain money values: subtotalMinor, taxMinor, deliveryFeeMinor, discountMinor, totalMinor, unitPriceMinor.

Tax is calculated based on the store's taxRateBps (basis points, e.g., 2500 = 25% Danish VAT) and taxInclusive setting.