> ## Documentation Index
> Fetch the complete documentation index at: https://docs.craveup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Order Tracking

> Poll order status and display real-time updates to customers.

After a customer completes checkout, keep them informed with order status updates. This guide covers how to poll for status changes and display progress in your storefront.

## Order status flow

Orders progress through these statuses:

```
OPEN → LOCKED → COMPLETED
```

| Status      | Meaning                                          |
| ----------- | ------------------------------------------------ |
| `OPEN`      | Cart is active — items can be added or modified  |
| `LOCKED`    | Payment is being processed — cart is frozen      |
| `COMPLETED` | Payment confirmed — order sent to the restaurant |

## Poll for status updates

After the customer completes payment, poll the cart endpoint to check for status changes.

<CodeGroup>
  ```ts SDK theme={null}
  import { storefront } from '@/lib/storefront';

  async function pollOrderStatus(locationId: string, cartId: string) {
    const poll = setInterval(async () => {
      const cart = await storefront.cart.get(locationId, cartId);

      if (cart.status === 'COMPLETED') {
        clearInterval(poll);
        // Show confirmation screen
        console.log('Order completed!');
      } else if (cart.status === 'LOCKED') {
        // Payment is processing
        console.log('Processing payment...');
      }
    }, 3000); // Poll every 3 seconds

    // Stop after 2 minutes to avoid infinite polling
    setTimeout(() => clearInterval(poll), 120_000);
  }
  ```

  ```bash REST theme={null}
  # Poll the cart endpoint
  curl -X GET "https://api.craveup.com/api/v1/locations/loc_123/carts/{cartId}" \
    -H "X-API-Key: $CRAVEUP_API_KEY"
  ```
</CodeGroup>

## Build a status component

```tsx filename="components/OrderStatus.tsx" theme={null}
'use client';

import { useEffect, useState } from 'react';
import { storefront } from '@/lib/storefront';

const STEPS = [
  { status: 'OPEN', label: 'Order placed' },
  { status: 'LOCKED', label: 'Processing payment' },
  { status: 'COMPLETED', label: 'Confirmed' },
];

export function OrderStatus({
  locationId,
  cartId,
}: {
  locationId: string;
  cartId: string;
}) {
  const [currentStatus, setCurrentStatus] = useState<string>('OPEN');

  useEffect(() => {
    const poll = setInterval(async () => {
      try {
        const cart = await storefront.cart.get(locationId, cartId);
        setCurrentStatus(cart.status ?? 'OPEN');
        if (cart.status === 'COMPLETED') clearInterval(poll);
      } catch {
        // Cart may be unavailable after completion
        clearInterval(poll);
      }
    }, 3000);

    return () => clearInterval(poll);
  }, [locationId, cartId]);

  const currentIndex = STEPS.findIndex((s) => s.status === currentStatus);

  return (
    <div className="space-y-4">
      {STEPS.map((step, i) => (
        <div key={step.status} className="flex items-center gap-3">
          <div
            className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold ${
              i <= currentIndex
                ? 'bg-teal-600 text-white'
                : 'bg-gray-200 text-gray-500'
            }`}
          >
            {i + 1}
          </div>
          <span className={i <= currentIndex ? 'font-medium' : 'text-gray-400'}>
            {step.label}
          </span>
        </div>
      ))}
    </div>
  );
}
```

## Confirmation page

After Stripe redirects back to your `return_url`, extract the cart ID and show the order summary.

```tsx filename="app/order/confirmation/page.tsx" theme={null}
import { storefront } from '@/lib/storefront';

export default async function ConfirmationPage({
  searchParams,
}: {
  searchParams: { cartId?: string };
}) {
  const cartId = searchParams.cartId;
  if (!cartId) return <p>Order not found.</p>;

  const locationId = process.env.NEXT_PUBLIC_LOCATION_ID!;
  const cart = await storefront.cart.get(locationId, cartId);

  return (
    <main className="max-w-lg mx-auto p-8">
      <h1 className="text-2xl font-bold">Order Confirmed</h1>
      <p className="text-gray-600 mt-2">
        Thank you! Your order from {cart.restaurantDisplayName} is on its way.
      </p>

      <div className="mt-6 space-y-3">
        {cart.items.map((item) => (
          <div key={item.id} className="flex justify-between">
            <span>{item.name} x{item.quantity}</span>
            <span>{item.totalFormatted}</span>
          </div>
        ))}
      </div>

      <div className="mt-6 pt-4 border-t">
        <div className="flex justify-between font-bold text-lg">
          <span>Total</span>
          <span>{cart.orderTotalWithServiceFeeFormatted}</span>
        </div>
      </div>
    </main>
  );
}
```

## Track analytics

Fire an `ORDER_PLACED` analytics event for conversion tracking.

```ts theme={null}
await storefront.analyticsEvents.track(locationId, {
  cartId,
  eventType: 'ORDER_PLACED',
  metadata: {
    total: cart.orderTotalWithServiceFee,
    currency: cart.currency,
    itemCount: cart.totalQuantity,
  },
});
```

## Submit a rating

After the order is complete, prompt the customer to rate their experience.

<CodeGroup>
  ```ts SDK theme={null}
  const response = await storefront.locations.submitRating('loc_123', {
    rating: 5,
    cartId,
    comment: 'Great food and fast delivery!',
  });
  ```

  ```bash REST theme={null}
  curl -X POST "https://api.craveup.com/api/v1/locations/loc_123/ratings" \
    -H "X-API-Key: $CRAVEUP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"rating": 5, "cartId": "cart_456", "comment": "Great food!"}'
  ```
</CodeGroup>

## Best practices

* **Poll interval**: 3 seconds is a good balance between responsiveness and API load
* **Timeout**: Stop polling after 2 minutes to prevent zombie intervals
* **Error handling**: Gracefully handle network errors during polling
* **Cleanup**: Always clear intervals in `useEffect` cleanup
* **Caching**: Store the final cart state locally to avoid unnecessary refetches on the confirmation page

## Next steps

<CardGroup cols={2}>
  <Card title="Error Codes" icon="triangle-exclamation" href="/guides/error-codes">
    Handle API errors throughout the ordering flow.
  </Card>

  <Card title="SDK Reference" icon="code" href="/getting-started/storefront-sdk">
    Full SDK documentation with all modules.
  </Card>
</CardGroup>
