---
title: "Order placed"
method: POST
path: "/orders/placed"
tags: ["Orders"]
---

# Order placed

`POST /orders/placed`

Send an order to Privy when a customer completes a purchase.

Privy uses `order_id` to decide whether to create or update the order.
The first request for an `order_id` creates the order and returns `201 Created`.
Later requests with the same `order_id` update it and return `200 OK`.
When updating an order, send its latest data. If you leave out an optional
field, Privy keeps its existing value.

Privy links the order to a contact using `email`. If you do not send an email
address, it uses `phone` instead. If no matching contact exists, Privy creates one.

`accepts_email_marketing` can subscribe the contact to email but cannot
unsubscribe an existing contact. `accepts_sms_marketing` records an SMS opt-in
only for newly created contacts and requires `phone`. It does not change the SMS
consent of an existing contact.

To associate the order with a specific Custom Integration, include the optional
`X-Privy-Integration-Token` header. See the header description below for setup
instructions and fallback behavior.

**Required scope:** `orders_write`

## Headers

- `X-Privy-Integration-Token` string

## Request body

- PlaceOrderRequest — `order_id`, `total`, `currency`, and `order_date` are required on every call, plus at least one of `email` or `phone`. All other fields are optional; on an update, omitted optional fields keep their previously stored value.
  - `order_id` integer, required — Your unique identifier for the order. Must be a positive integer (a digits-only string is also accepted). This is the idempotency key: the first call for an `order_id` creates the order, subsequent calls update it.
  - `email` string, email — Buyer's email. Used to find or create the associated contact. At least one of `email` or `phone` is required.
  - `phone` string — Buyer's phone number. Loosely formatted input is accepted and normalized to E.164. Required when `accepts_sms_marketing` is `true`.
  - `customer_id` string — Your external identifier for the customer.
  - `subtotal` number — Order subtotal before tax, shipping, and discounts.
  - `discounts` Discount[] — Discounts applied to the order.
    - `code` string
    - `amount` number
  - `tax_lines` TaxLine[] — Tax lines applied to the order.
    - `title` string
    - `amount` number
  - `total` number, required — Order grand total. Required.
  - `currency` string, required — ISO 4217 three-letter currency code. Required.
  - `total_items` integer — Total number of items in the order.
  - `financial_status` 'authorized' | 'expired' | 'paid' | 'partially_paid' | 'partially_refunded' | 'pending' | 'refunded' | 'voided' — Payment status of the order.
  - `fulfillment_status` 'fulfilled' | 'in_progress' | 'on_hold' | 'open' | 'partially_fulfilled' | 'pending' | 'pending_fulfillment' | 'request_declined' | 'restocked' | 'scheduled' | 'unfulfilled' — The order's overall fulfillment state (mirrors Shopify's OrderDisplayFulfillmentStatus). Stored on the order but does not affect delivery-based flow triggers — use `shipment_status` for that.
  - `shipment_status` 'attempted_delivery' | 'carrier_picked_up' | 'confirmed' | 'delayed' | 'delivered' | 'failure' | 'in_transit' | 'label_printed' | 'label_purchased' | 'out_for_delivery' | 'picked_up' | 'ready_for_pickup' — Carrier delivery status of the shipment (mirrors Shopify's FulfillmentEventStatus). Setting this to `delivered` triggers any "Order Received" flows for the customer.
  - `line_items` LineItem[] — The products purchased.
    - `product_id` number
    - `variant_id` number
    - `sku` string
    - `title` string
    - `quantity` integer
    - `price` number
  - `billing_address` Address — A billing or shipping address. Stored and echoed back verbatim. When a new contact is created, its name is taken from the address — shipping first, billing as fallback — using `first_name`/`last_name`, or a single `name` field split on whitespace.
    - `first_name` string
    - `last_name` string
    - `name` string — Full name; used when `first_name`/`last_name` are absent.
    - `address1` string
    - `address2` string
    - `city` string
    - `state_code` string
    - `country_code` string
    - `postal_code` string
  - `shipping_address` Address — A billing or shipping address. Stored and echoed back verbatim. When a new contact is created, its name is taken from the address — shipping first, billing as fallback — using `first_name`/`last_name`, or a single `name` field split on whitespace.
    - `first_name` string
    - `last_name` string
    - `name` string — Full name; used when `first_name`/`last_name` are absent.
    - `address1` string
    - `address2` string
    - `city` string
    - `state_code` string
    - `country_code` string
    - `postal_code` string
  - `accepts_email_marketing` boolean — Whether the buyer opted into email marketing. Subscribes the contact on signup; never unsubscribes an existing contact.
  - `accepts_sms_marketing` boolean — Whether the buyer opted into SMS marketing. Requires `phone`. Records an SMS opt-in for newly created contacts only; existing contacts' SMS consent is left untouched.
  - `order_date` string, date-time, required — ISO 8601 date-time the order was placed. Required. Expected to be in UTC (zero offset) — end the timestamp with `Z` (e.g. `2026-06-01T12:00:00Z`).
  - `initial_sync` boolean — Set to `true` for bulk historical imports. Suppresses real-time side effects (Flows and other automations, and campaign-revenue attribution) while still recording the order, associating the contact, and updating the contact's `order_count`, `first_order_at`, and `last_order_at` values.

## Response `200`

Existing order updated.

- object
  - `data` Order — An order as persisted. Echoes back the fields you sent (column-backed fields normalized, the rest verbatim). Empty values are omitted from the response.
    - `order_id` integer — Your order identifier, echoed back. The only public identifier for the order.
    - `source` string
    - `email` string, email
    - `phone` string — Normalized to E.164.
    - `total_amount` string — Order grand total, as a decimal string.
    - `currency_code` string
    - `placed_at` string, date-time
    - `financial_status` string
    - `fulfillment_status` string
    - `shipment_status` string
    - `customer_id` string
    - `subtotal` number
    - `discounts` Discount[]
      - `code` string
      - `amount` number
    - `tax_lines` TaxLine[]
      - `title` string
      - `amount` number
    - `total_items` integer
    - `line_items` LineItem[]
      - `product_id` number
      - `variant_id` number
      - `sku` string
      - `title` string
      - `quantity` integer
      - `price` number
    - `billing_address` Address — A billing or shipping address. Stored and echoed back verbatim. When a new contact is created, its name is taken from the address — shipping first, billing as fallback — using `first_name`/`last_name`, or a single `name` field split on whitespace.
      - `first_name` string
      - `last_name` string
      - `name` string — Full name; used when `first_name`/`last_name` are absent.
      - `address1` string
      - `address2` string
      - `city` string
      - `state_code` string
      - `country_code` string
      - `postal_code` string
    - `shipping_address` Address — A billing or shipping address. Stored and echoed back verbatim. When a new contact is created, its name is taken from the address — shipping first, billing as fallback — using `first_name`/`last_name`, or a single `name` field split on whitespace.
      - `first_name` string
      - `last_name` string
      - `name` string — Full name; used when `first_name`/`last_name` are absent.
      - `address1` string
      - `address2` string
      - `city` string
      - `state_code` string
      - `country_code` string
      - `postal_code` string
    - `accepts_email_marketing` boolean
    - `accepts_sms_marketing` boolean
    - `integration` OrderIntegration — The Custom Integration the order is tied to. Present only when the order was attributed to an integration (via the `X-Privy-Integration-Token` header or auto-tie).
      - `store_name` string
      - `store_url` string
    - `created_at` string, date-time
    - `updated_at` string, date-time

## Other responses

- `201` — Order created.
- `401` — Missing or invalid bearer token.
- `403` — Token lacks the required scope for this endpoint.
- `422` — One or more fields failed validation.
- `429` — Rate limit exceeded. Retry after the specified time.

---

[API](https://skmtc.dev/privy/apis/privy-api.md) · [All operations](https://skmtc.dev/privy/apis/privy-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/privy/privy-api/revisions/5378aff56b9a/schema)
