Every endpoint is rate-limited (300 req/min) and idempotent for mutations via the Idempotency-Key header. Webhook payloads are signed with HMAC-SHA256 and must be verified before processing.

Orders

POST
/v1/orders

Create an order from a room

Request
curl -X POST https://api.castille.mt/v1/orders \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -d '{"roomNumber":204,"category":"fnb","items":[{"id":"m1","qty":1}]}'
Response 200
{
  "id": "o_3RAa9",
  "status": "pending",
  "total": 28,
  "paymentUrl": "https://pay.stripe.com/..."
}
GET
/v1/orders?status=pending

List orders with optional filters

Request
curl https://api.castille.mt/v1/orders?status=pending \
  -H "Authorization: Bearer sk_live_••••"
Response 200
{
  "data": [
    {
      "id": "o_3RAa9",
      "roomNumber": 204,
      "category": "fnb",
      "status": "pending",
      "total": 28
    }
  ],
  "page": 1,
  "total": 24
}
GET
/v1/orders/{id}

Retrieve a single order

Request
curl https://api.castille.mt/v1/orders/{id} \
  -H "Authorization: Bearer sk_live_••••"
Response 200
{
  "id": "o_3RAa9",
  "status": "preparing",
  "items": [
    {
      "name": "Lampuki",
      "qty": 1
    }
  ]
}
PATCH
/v1/orders/{id}

Update status

Request
curl -X PATCH https://api.castille.mt/v1/orders/{id} \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -d '{"status":"preparing"}'
Response 200
{
  "id": "o_3RAa9",
  "status": "preparing"
}
DELETE
/v1/orders/{id}

Cancel and refund order

Request
curl https://api.castille.mt/v1/orders/{id} \
  -H "Authorization: Bearer sk_live_••••"
Response 200
{
  "id": "o_3RAa9",
  "status": "cancelled",
  "refundId": "re_1NaB2"
}

Rooms

GET
/v1/rooms

List rooms with QR URLs

Request
curl https://api.castille.mt/v1/rooms \
  -H "Authorization: Bearer sk_live_••••"
Response 200
{
  "data": [
    {
      "id": "r1",
      "number": 101,
      "qrUrl": "https://castille.app/room/101"
    }
  ]
}
GET
/v1/rooms/{number}/qr.png

Download a room's QR code as PNG (1024×1024)

Request
curl https://api.castille.mt/v1/rooms/{number}/qr.png \
  -H "Authorization: Bearer sk_live_••••"
Response 200
{
  "contentType": "image/png",
  "size": "12kb"
}

Notifications

POST
/v1/notify/whatsapp

Internal — send a templated WhatsApp message

Request
curl -X POST https://api.castille.mt/v1/notify/whatsapp \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -d '{"to":"+356 9900 2201","template":"order_received","vars":{"room":204,"total":"€28"}}'
Response 200
{
  "messageId": "wamid.abc",
  "status": "queued"
}
POST
/v1/notify/email

Internal — send transactional email

Request
curl -X POST https://api.castille.mt/v1/notify/email \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -d '{"to":"owner@castille.mt","template":"new_order","vars":{"id":"o_3RAa9"}}'
Response 200
{
  "messageId": "em_1Nx",
  "status": "sent"
}

Payments

POST
/v1/payments/checkout

Create Stripe Checkout session for an order

Request
curl -X POST https://api.castille.mt/v1/payments/checkout \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -d '{"orderId":"o_3RAa9"}'
Response 200
{
  "url": "https://checkout.stripe.com/c/pay/cs_test_..."
}
POST
/v1/webhooks/stripe

Webhook — signed event from Stripe

Request
curl https://api.castille.mt/v1/webhooks/stripe \
  -H "Authorization: Bearer sk_live_••••"
Response 200
{
  "received": true
}