> This page is for Documentation.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fiftytwo-demo.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fiftytwo-demo.ferndocs.com/_mcp/server.

# Import webshop sales into the POS

A customer buys online and wants to return the item to a shop. The shop assistant
scans the receipt, and the till has never heard of the sale.

[Register Sale](/api/register-sale/overview) fixes that. It imports finished sales from
external systems (webshops, self-checkout solutions) into ViKING POS, so the
transaction exists in the estate and stores can act on it.

> **Note**
>
> "Finished" is the operative word. This API is for sales that have already
> completed and been paid for elsewhere. To *price* a basket before payment, use
> [Basket API](/api/basket/overview).

## Two endpoints

```
POST /v1/sale                     submit a sale, get a correlation ID
GET  /v1/sale/{correlationId}     check its processing status
```

The base URL is `https://api.fiftytwo.com/register-sale-api`.

> **Warning**
>
> Register Sale authenticates with an **Auth0-issued** Bearer JWT, not a token
> from `auth.fiftytwo.com`. The header format is identical; the issuer is not.
> Ask your FiftyTwo consultant which tenant issues yours.

## The flow

Processing is asynchronous. Submitting a sale returns immediately with a
correlation ID; the import happens after.

#### Submit the sale

```bash
curl -X POST https://api.fiftytwo.com/register-sale-api/v1/sale \
  -H "Authorization: Bearer $AUTH0_TOKEN" \
  -H "Content-Type: application/json" \
  -d @sale.json
```

You get a correlation ID back. The sale is now `Pending`.

#### Poll for a terminal status

```bash
curl "https://api.fiftytwo.com/register-sale-api/v1/sale/$CORRELATION_ID" \
  -H "Authorization: Bearer $AUTH0_TOKEN"
```

A sale transitions from `Pending` to `Processed` or `Failed`. Those two are
terminal, so stop polling when you see either.

#### Handle Failed explicitly

`Failed` is a real outcome, not a transport error. Something about the sale
was rejected. Do not retry blindly; surface it.

## Idempotency is your job

> **Warning**
>
> If a `POST /v1/sale` times out, you do not know whether the sale was accepted.
> Retrying can import it twice, and a duplicate sale means a customer could
> return the same goods twice.

Persist the correlation ID before you consider the submission done, and key your
retry logic on your own order ID:

```python
import requests

BASE = "https://api.fiftytwo.com/register-sale-api"

def register_sale(order, token, store):
    """Submit a sale at most once per order, even across retries."""
    existing = store.get_correlation_id(order.id)
    if existing:
        return existing                     # already submitted; just poll it

    resp = requests.post(
        f"{BASE}/v1/sale",
        headers={"Authorization": f"Bearer {token}"},
        json=order.to_payload(),
        timeout=30,
    )
    resp.raise_for_status()
    correlation_id = resp.json()["correlationId"]

    # Record before returning: a crash here is recoverable, a lost ID is not.
    store.save_correlation_id(order.id, correlation_id)
    return correlation_id
```

If the POST times out with no ID recorded, reconcile before resubmitting,
ideally by checking whether the sale already exists via
[Transaction POS Log](/api/transactions/overview).

## Poll with backoff

```python
import time

TERMINAL = {"Processed", "Failed"}

def wait_for(correlation_id, token, timeout=300):
    deadline = time.monotonic() + timeout
    delay = 1.0
    while time.monotonic() < deadline:
        resp = requests.get(
            f"{BASE}/v1/sale/{correlation_id}",
            headers={"Authorization": f"Bearer {token}"},
            timeout=15,
        )
        resp.raise_for_status()
        status = resp.json()["status"]
        if status in TERMINAL:
            return status
        time.sleep(delay)
        delay = min(delay * 1.6, 15.0)
    raise TimeoutError(f"{correlation_id} still pending after {timeout}s")
```

Poll from a background worker, not from the request that completes the customer's
checkout. The customer should not wait on POS import.

## What the store sees

Once processed, the sale is a transaction in the estate like any other. A shop
assistant can find it and process a return against it through the normal returns
flow. See [Returns and corrections](/help/concepts/returns-corrections)
and [Handle returns](/help/pos/returns).

## Choosing between this and the Receipt API

The [Receipt API](/api/transactions/receipt-api) also accepted posted receipts from
external systems, and it is **deprecated**. New integrations should use Register
Sale for writing sales in, and
[Transaction POS Log](/api/transactions/overview) for reading transactions out.

## Related help-center topics

#### [Returns and corrections](/help/concepts/returns-corrections)

How returns work in 52ViKING.

#### [Handle returns](/help/pos/returns)

What the shop assistant actually does.