R
Docs
API Documentation

SDK examples — Node, Python, PHP

Submit an order in four languages — paste, swap in your tenant ID, and you have a working integration.

2 min read Updated 5/30/2026developer

Overview

These snippets target the public order submit endpoint — the most-asked-about integration in production. Every example sends the same payload; the only thing that changes is the HTTP client.

The endpoint is anonymous-safe (no auth header required); coupon codes, gift cards, and the Phase 55 customerCoords field are all optional. See api/orders for the full body schema.

Want machine-readable shapes? Download the Postman collection or OpenAPI 3.0 spec and generate clients with openapi-generator.

cURL

Shell
curl -X POST 'https://restora360.com/api/public/luigi-pizza/orders' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "delivery",
    "customerName": "Jane Doe",
    "customerEmail": "jane@example.com",
    "customerPhone": "+44 7700 900123",
    "deliveryAddress": "12 Example Street",
    "postcode": "SW1A 1AA",
    "paymentMethod": "cash",
    "items": [
      { "productId": "p_abc123", "productName": "Margherita pizza", "quantity": 1, "price": 12.5 }
    ]
  }'

The server re-validates every item price against the menu — your local price field is informational, not authoritative.

Node.js (fetch)

Works on Node 18+ (native fetch) and any recent Bun / Deno runtime. No dependencies.

javascript
const TENANT_ID = 'luigi-pizza';

async function submitOrder() {
  const response = await fetch(`https://restora360.com/api/public/${TENANT_ID}/orders`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'delivery',
      customerName: 'Jane Doe',
      customerEmail: 'jane@example.com',
      customerPhone: '+44 7700 900123',
      deliveryAddress: '12 Example Street',
      postcode: 'SW1A 1AA',
      paymentMethod: 'cash',
      items: [
        { productId: 'p_abc123', productName: 'Margherita pizza', quantity: 1, price: 12.5 },
      ],
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      throw new Error(`Rate limited — retry in ${retryAfter}s`);
    }
    throw new Error(error.message ?? error.error);
  }

  return response.json();
}

submitOrder().then(console.log).catch(console.error);

Python (requests)

Tested against Python 3.10+ and requests 2.31. Install with pip install requests.

python
import requests

TENANT_ID = "luigi-pizza"
BASE_URL = "https://restora360.com"

def submit_order():
    response = requests.post(
        f"{BASE_URL}/api/public/{TENANT_ID}/orders",
        json={
            "type": "delivery",
            "customerName": "Jane Doe",
            "customerEmail": "jane@example.com",
            "customerPhone": "+44 7700 900123",
            "deliveryAddress": "12 Example Street",
            "postcode": "SW1A 1AA",
            "paymentMethod": "cash",
            "items": [
                {
                    "productId": "p_abc123",
                    "productName": "Margherita pizza",
                    "quantity": 1,
                    "price": 12.5,
                }
            ],
        },
        timeout=10,
    )

    if response.status_code == 429:
        retry_after = response.headers.get("Retry-After", "60")
        raise RuntimeError(f"Rate limited — retry in {retry_after}s")

    response.raise_for_status()
    return response.json()

if __name__ == "__main__":
    print(submit_order())

PHP (cURL)

No Composer dependencies — uses the cURL extension shipped with every PHP install.

php
<?php

const TENANT_ID = 'luigi-pizza';
const BASE_URL  = 'https://restora360.com';

function submitOrder(): array {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => BASE_URL . '/api/public/' . TENANT_ID . '/orders',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS     => json_encode([
            'type'            => 'delivery',
            'customerName'    => 'Jane Doe',
            'customerEmail'   => 'jane@example.com',
            'customerPhone'   => '+44 7700 900123',
            'deliveryAddress' => '12 Example Street',
            'postcode'        => 'SW1A 1AA',
            'paymentMethod'   => 'cash',
            'items'           => [
                ['productId' => 'p_abc123', 'productName' => 'Margherita pizza', 'quantity' => 1, 'price' => 12.5],
            ],
        ]),
        CURLOPT_TIMEOUT        => 10,
    ]);

    $body = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($code === 429) {
        throw new RuntimeException('Rate limited — retry later');
    }
    if ($code >= 400) {
        throw new RuntimeException("HTTP $code: $body");
    }

    return json_decode($body, true);
}

print_r(submitOrder());

Next steps

  • Idempotency — set the Idempotency-Key header to a stable per-attempt UUID so network retries don't double-submit. See api/idempotency.
  • Polling order status — pass the returned order.id to GET /api/public/{tenantId}/orders/{orderId}. Webhook events are the better long-term path; see api-webhooks/overview.
  • Rate limits — back off on Retry-After rather than retrying immediately; the limiter slides forward in real time, not by fixed buckets. See api/rate-limits.

Frequently asked

  • The tenant ID is the subdomain (e.g. `luigi-pizza` for `luigi-pizza.restora360.com`). Operators can find theirs in **Settings → Domain** in the dashboard.