Sending us your commerce data

The Custom Commerce API. If you are on Shopify we read your store for you; on anything else, push the same records here and everything downstream works the same way.

What you need

One API key with the events.write scope, made in your TryMe settings under API keys. The key is shown once, when you create it.

curl -X POST https://api.tryme.com/api/v1/events \
  -H "Authorization: Bearer tk_live_..." \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d @events.json

The envelope

Send up to 100 events per request. Each one is a type, the moment it happened, and the record itself under data.

{
  "events": [
    {
      "type": "purchase.completed",
      "occurredAt": "2027-03-02T14:21:00Z",
      "data": {
        "id": "ord-90412",
        "currency": "GBP",
        "total": "128.00",
        "discount": "10.00",
        "discountCodes": [
          "TRYME-8QK2VP"
        ],
        "customerId": "cus-4181",
        "attributes": {
          "tryme_journey": "jny_01HW3Z6K2P"
        },
        "lineItems": [
          {
            "productId": "sku-harbour-fog",
            "quantity": 1,
            "unitPrice": "110.00"
          }
        ]
      }
    }
  ]
}

A batch is all or nothing. If any event is rejected, none are accepted, and the response lists every problem with the index of the event it came from. Idempotency keys here are positional, so a partial success would leave you no safe way to retry.

Money and dates

Amounts are decimal, in the currency's main unit. "12.80" is twelve pounds eighty. Not minor units: a contract that took 1280 here and 12.80 from a platform would put a hundredfold error one typo away. Send them as strings if you can, so nothing rounds on the way.

Dates are ISO 8601. occurredAt is optional and defaults to when we received the event. A date we cannot parse is refused rather than replaced, because an integration sending an unreadable date has a bug worth knowing about.

What you can send

EventCarries
commerce.product_updatedproduct
commerce.product_deletedproduct
commerce.customer_updatedcustomer
commerce.order_createdorder
commerce.order_cancelledorder
purchase.completedpurchase
purchase.refundedrefund
fulfillment.shippedfulfillment
fulfillment.deliveredfulfillment
fulfillment.failedfulfillment

Nothing else. The event taxonomy is larger, but the rest of it is things TryMe concludes or does, and a key that could assert those would be writing its own results.

Product

FieldNotes
idRequired. Your own product id. We never mint one for you.
titleRequired.
status`active`, `draft` or `archived`. Defaults to `active`.
currencyISO 4217, e.g. `GBP`. Needed when you send any price.
priceUsed for the default variant when you send no variants.
variants[].idRequired on each variant. A trial is mapped to a variant.
variants[].priceDecimal, main unit. `"12.80"` is twelve pounds eighty.
variants[].inventoryQuantityLeave it out when you do not track stock.

commerce.product_updated

{
  "type": "commerce.product_updated",
  "occurredAt": "2027-03-01T09:00:00Z",
  "data": {
    "id": "sku-harbour-fog",
    "title": "Harbour Fog Eau de Parfum",
    "status": "active",
    "handle": "harbour-fog",
    "vendor": "Northaven",
    "currency": "GBP",
    "imageUrl": "https://cdn.northaven.test/harbour-fog.jpg",
    "variants": [
      {
        "id": "hf-50",
        "title": "50ml",
        "sku": "HF-50",
        "price": "128.00",
        "inventoryQuantity": 24
      },
      {
        "id": "hf-100",
        "title": "100ml",
        "sku": "HF-100",
        "price": "180.00",
        "available": false
      }
    ]
  }
}

commerce.product_deleted

{
  "type": "commerce.product_deleted",
  "occurredAt": "2027-03-01T09:05:00Z",
  "data": {
    "id": "sku-harbour-fog",
    "title": "Harbour Fog Eau de Parfum"
  }
}

Order

FieldNotes
idRequired. Your own order id, and what every later event refers to.
currencyRequired. ISO 4217.
totalRequired. Decimal, main unit, including tax.
discountCodesSend these. A trial credit is recognised by its code.
attributesWhatever the checkout carried, including `tryme_journey`.
lineItems[].quantityRequired on each line.
lineItems[].productIdSend it where you have it. Attribution reads it.
placedAtISO 8601. Defaults to the event time.

commerce.order_created

{
  "type": "commerce.order_created",
  "occurredAt": "2027-03-02T14:20:00Z",
  "data": {
    "id": "ord-90412",
    "number": "#1042",
    "currency": "GBP",
    "total": "128.00",
    "subtotal": "110.00",
    "tax": "18.00",
    "customerId": "cus-4181",
    "email": "rowan@fernlake.test",
    "lineItems": [
      {
        "productId": "sku-harbour-fog",
        "variantId": "hf-50",
        "quantity": 1,
        "unitPrice": "110.00"
      }
    ]
  }
}

commerce.order_cancelled

{
  "type": "commerce.order_cancelled",
  "occurredAt": "2027-03-02T16:00:00Z",
  "data": {
    "id": "ord-90412",
    "currency": "GBP",
    "total": "128.00",
    "cancelReason": "customer_changed_mind"
  }
}

Customer

FieldNotes
idRequired. Your own customer id.
emailHashed on arrival. We hold identities, not address books.
acceptsMarketingOnly send it if you know. Absent is not false.

commerce.customer_updated

{
  "type": "commerce.customer_updated",
  "occurredAt": "2027-03-01T09:10:00Z",
  "data": {
    "id": "cus-4181",
    "email": "rowan@fernlake.test",
    "firstName": "Rowan",
    "lastName": "Ash",
    "acceptsMarketing": true
  }
}

Fulfillment

FieldNotes
idRequired. Your own fulfilment or shipment id.
orderIdRequired. The order this parcel is for.
trackingNumberOptional, and shown to the shopper where you send it.
shippedAtISO 8601. Defaults to the event time.

fulfillment.shipped

{
  "type": "fulfillment.shipped",
  "occurredAt": "2027-03-03T08:00:00Z",
  "data": {
    "id": "shp-771",
    "orderId": "ord-90412",
    "carrier": "Fernlake Post",
    "trackingNumber": "FP-4471192",
    "trackingUrl": "https://track.fernlake.test/FP-4471192"
  }
}

fulfillment.delivered

{
  "type": "fulfillment.delivered",
  "occurredAt": "2027-03-05T10:30:00Z",
  "data": {
    "id": "shp-771",
    "orderId": "ord-90412",
    "carrier": "Fernlake Post"
  }
}

fulfillment.failed

{
  "type": "fulfillment.failed",
  "occurredAt": "2027-03-05T10:30:00Z",
  "data": {
    "id": "shp-771",
    "orderId": "ord-90412",
    "carrier": "Fernlake Post"
  }
}

Purchase

FieldNotes
The same object as `order`. The event is what says the money moved.

purchase.completed

{
  "type": "purchase.completed",
  "occurredAt": "2027-03-02T14:21:00Z",
  "data": {
    "id": "ord-90412",
    "currency": "GBP",
    "total": "128.00",
    "discount": "10.00",
    "discountCodes": [
      "TRYME-8QK2VP"
    ],
    "customerId": "cus-4181",
    "attributes": {
      "tryme_journey": "jny_01HW3Z6K2P"
    },
    "lineItems": [
      {
        "productId": "sku-harbour-fog",
        "quantity": 1,
        "unitPrice": "110.00"
      }
    ]
  }
}

Refund

FieldNotes
idRequired. Your own refund id.
orderIdRequired. The order the money went back on.
amountRequired. Decimal, main unit. What actually went back.
partialWhether only part of the order came back.
orderTotalSend this instead of `partial` and we work it out.

purchase.refunded

{
  "type": "purchase.refunded",
  "occurredAt": "2027-03-09T11:00:00Z",
  "data": {
    "id": "ref-2290",
    "orderId": "ord-90412",
    "amount": "28.00",
    "currency": "GBP",
    "orderTotal": "128.00",
    "reason": "damaged_in_transit"
  }
}

When something is wrong

A rejected batch answers 400 with code: "invalid_request" and a problem per bad field, named by where it sits in what you sent.

{
  "ok": false,
  "code": "invalid_request",
  "error": "The request body is not what this endpoint expects.",
  "details": {
    "problems": [
      {
        "event": 0,
        "path": "data.total",
        "problem": "is required"
      },
      {
        "event": 1,
        "path": "data.lineItems[0].quantity",
        "problem": "must be a whole number, zero or more"
      }
    ]
  }
}

Order of events

Send the order before the fulfilment and the fulfilment before the refund where you can, but nothing breaks if you do not: every consumer is idempotent and out-of-order delivery is normal. What matters is that the ids match. orderId on a fulfilment or a refund is the id you sent on the order.