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

# Creating Transactions

> How to record completed transactions in Omneo via the API, single, upsert, and bulk patterns.

Transactions in Omneo represent completed purchases. Sending a transaction links it to a customer profile, contributes to aggregations (lifetime spend, ATV, purchase frequency), triggers reactions, and makes the transaction visible in Clienteling and Profile Portal.

<Note>
  Only sync completed transactions to Omneo. Do not sync draft, partially paid, or otherwise incomplete records. Omneo transactions are immutable records of what occurred.
</Note>

## Before you start

* Review [Authentication](/dev-guides/core-setup/authentication) to get a bearer token
* Plan your data mapping: decide which transaction fields from your source system map to Omneo fields
* Determine whether you'll look up profiles by Omneo ID, email, or an external identity handle

## Transaction anatomy

A transaction has two levels:

* **Header**: overall transaction details: total, date, location, staff, receipt ref, payments, tags
* **Items**: individual line items with product, price, and quantity

Both header and items can be provided in the same request.

## Resolving the profile ID

You don't need to know the Omneo profile ID up front. Use `profile_id_handle` to tell Omneo how to interpret the `profile_id` field:

| `profile_id_handle` | Behaviour                                                                        |
| ------------------- | -------------------------------------------------------------------------------- |
| *(omitted)*         | `profile_id` is treated as the Omneo profile UUID                                |
| `"email"`           | `profile_id` is treated as an email address                                      |
| Any other string    | `profile_id` is treated as an identity value for that handle (e.g., `"shopify"`) |

## Creating a transaction

```shell theme={null}
curl -X POST https://api.[tenant].getomneo.com/api/v3/transactions \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "jane@example.com",
    "profile_id_handle": "email",
    "total": 149.95,
    "transacted_at": "2025-06-15 14:32:00",
    "timezone": "Australia/Melbourne",
    "external_id": "ORDER-10333",
    "receipt_ref": "659",
    "location_id": 2,
    "staff_id": 1993,
    "payments": ["EFTPOS"],
    "items": [
      {
        "name": "Linen Shirt",
        "product_variant_id": 333729,
        "quantity": 1,
        "price_current": 149.95,
        "price_sell": 149.95,
        "price_original": 149.95,
        "is_void": false
      }
    ]
  }'
```

## Upsert (create or update)

Use the sync endpoint when you have a reliable `external_id` and want to avoid duplicate transactions:

```shell theme={null}
POST /api/v3/transactions/sync
```

This searches for an existing transaction matching `external_id`. If found, it updates it. If not found, it creates a new one.

## Updating a transaction

```shell theme={null}
curl -X PUT https://api.[tenant].getomneo.com/api/v3/transactions/{transactionId} \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "total": 99.95
  }'
```

Only the fields you include will be updated.

## Voiding a transaction

Prefer voiding over deletion. Set `is_void: true` on the transaction to mark it as voided without removing it from history:

```shell theme={null}
curl -X PUT https://api.[tenant].getomneo.com/api/v3/transactions/{transactionId} \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "is_void": true
  }'
```

## Bulk / queued transactions

For historical imports or high-volume ingestion, use the queue endpoint rather than the standard create endpoint. This places each transaction into a batch processing queue rather than processing it synchronously:

```shell theme={null}
POST /api/v3/transactions/queue
```

The request body is identical to the standard create endpoint. Loop through your records and submit them individually to the queue. A `transaction.created` webhook fires as each one processes.

## Adding line items after creation

```shell theme={null}
curl -X POST https://api.[tenant].getomneo.com/api/v3/transactions/{transactionId}/items \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Belt",
    "product_variant_id": 1,
    "quantity": 1,
    "price_current": 49.95,
    "price_sell": 49.95,
    "price_original": 49.95,
    "is_void": false
  }'
```

Note: adding items after the fact does not automatically adjust the transaction `total`. Update the header separately if needed.

## Key fields reference

| Field           | Required    | Notes                                 |
| --------------- | ----------- | ------------------------------------- |
| `total`         | Yes         | Final transaction total               |
| `transacted_at` | Yes         | Datetime in UTC or with timezone      |
| `timezone`      | Recommended | IANA timezone string                  |
| `external_id`   | Recommended | Your system's transaction ID          |
| `receipt_ref`   | Recommended | The receipt number the customer knows |
| `profile_id`    | Recommended | Links the transaction to a profile    |
| `location_id`   | Recommended | Omneo location ID                     |
| `staff_id`      | Optional    | Omneo staff ID                        |
| `payments`      | Optional    | Array of payment method strings       |
| `items`         | Recommended | Array of line items                   |
