Skip to content
Public API — beta. The response schema may change without notice. v2 will be the first stable version, published once the public API leaves beta — evaluate on v1, but pin to v2 for production.

REST API

The REST API lets you work with your Logistified data over plain HTTP — send a request to a URL, get JSON back — without speaking MCP or writing code. It’s the same data and the same keys as AI / Agent access, exposed as ordinary REST resources.

https://prod.logistified.app/api/v1

Authenticate with an API key (see Authentication):

Terminal window
curl -s -H "Authorization: Bearer $LSTK_TOKEN" \
"https://prod.logistified.app/api/v1/purchase-orders?limit=5"

Every successful response is wrapped in a small envelope:

{
"data": { "purchaseOrders": [ /* … */ ], "totalCount": 42, "cursor": "2" },
"meta": { "requestId": "", "apiVersion": "1.1" }
}

A complete purchase order, start to finish. Each step is one call; the ids come from the previous response. You’ll need a write-scope key and the supplierId / locationId / variantId values from the app (or from AI / Agent access — the REST API has no reference-data endpoints yet).

  1. Create the draft. The body is the PO header plus, optionally, its opening line items.

    Terminal window
    curl -s -X POST "https://prod.logistified.app/api/v1/purchase-orders" \
    -H "Authorization: Bearer $LSTK_TOKEN" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
    "poName": "Q3 restock",
    "supplierId": "sup_8f21",
    "locationId": "loc_412",
    "lineItems": [{ "variantId": "var_9a01", "quantityOrdered": 30, "unitCost": 12.5 }]
    }'

    201 Created, with a Location: /api/v1/purchase-orders/po_7c3e header:

    {
    "data": {
    "purchaseOrder": {
    "purchaseOrderId": "po_7c3e",
    "poNumber": "PO-1042",
    "poName": "Q3 restock",
    "status": "Draft",
    "previousStatus": "Draft",
    "supplierId": "sup_8f21",
    "locationId": "loc_412",
    "itemCount": 1,
    "totalValue": 375,
    "lineItems": [
    {
    "poLineItemId": "pol_31aa",
    "variantId": "var_9a01",
    "quantityOrdered": 30,
    "quantityConfirmed": 0,
    "quantityReceived": 0,
    "quantityPending": 30,
    "unitCost": 12.5,
    "lineItemStatus": "Pending"
    }
    ]
    }
    },
    "meta": { "requestId": "9d1f…", "apiVersion": "1.1" }
    }
  2. Send it to the supplier.

    Terminal window
    curl -s -X POST ".../api/v1/purchase-orders/po_7c3e/status" \
    -H "Authorization: Bearer $LSTK_TOKEN" -H "Content-Type: application/json" \
    -d '{ "status": "Sent" }'
    {
    "data": {
    "purchaseOrder": { "purchaseOrderId": "po_7c3e", "status": "Sent", "previousStatus": "Draft", "lineItems": [ /* … */ ] },
    "transition": { "from": "Draft", "to": "Sent" },
    "sideEffects": { "incoming": { "outcome": "applied" } }
    },
    "meta": { "requestId": "b204…", "apiVersion": "1.1" }
    }
  3. Confirm what the supplier committed to. confirmAll accepts the ordered quantities as-is and moves the PO to In Progress; pass lines instead to confirm different amounts.

    Terminal window
    curl -s -X POST ".../api/v1/purchase-orders/po_7c3e/confirm" \
    -H "Authorization: Bearer $LSTK_TOKEN" -H "Content-Type: application/json" \
    -d '{ "confirmAll": true }'
    {
    "data": {
    "purchaseOrder": { "purchaseOrderId": "po_7c3e", "status": "In Progress", "lineItems": [ /* … */ ] },
    "status": { "before": "Sent", "after": "In Progress", "autoTransitioned": true },
    "updatedLineItemIds": ["pol_31aa"],
    "sideEffects": {}
    },
    "meta": { "requestId": "1e77…", "apiVersion": "1.1" }
    }
  4. Receive the goods. Quantities are the new totals for each line, not amounts to add — so sending the same call twice is safe.

    Terminal window
    curl -s -X POST ".../api/v1/purchase-orders/po_7c3e/receive" \
    -H "Authorization: Bearer $LSTK_TOKEN" -H "Content-Type: application/json" \
    -d '{ "lines": [{ "lineItemId": "pol_31aa", "quantityReceived": 30 }] }'
    {
    "data": {
    "purchaseOrder": { "purchaseOrderId": "po_7c3e", "status": "Received", "lineItems": [ /* … */ ] },
    "status": { "before": "In Progress", "after": "Received", "autoTransitioned": true },
    "sideEffects": {
    "shopifySync": { "status": "queued", "syncedLineItemCount": 1 },
    "incoming": { "outcome": "applied" }
    }
    },
    "meta": { "requestId": "c5a0…", "apiVersion": "1.1" }
    }
  5. Complete it. This also runs the cost sync back to your products.

    Terminal window
    curl -s -X POST ".../api/v1/purchase-orders/po_7c3e/complete" \
    -H "Authorization: Bearer $LSTK_TOKEN" -H "Content-Type: application/json" -d '{}'
    {
    "data": {
    "purchaseOrder": { "purchaseOrderId": "po_7c3e", "status": "Completed", "lineItems": [ /* … */ ] },
    "status": { "before": "Received", "after": "Completed" },
    "costSync": { "mode": "shopify", "syncedVariantCount": 1, "skippedCustomItemCount": 0, "warnings": [] },
    "sideEffects": {}
    },
    "meta": { "requestId": "77b2…", "apiVersion": "1.1" }
    }

The REST API and the Agent access surface share the same lstk_ API keys, the same scopes, and the same per-shop rate limit, and act on the same data with the same rules. Every REST write runs the identical operation an AI agent would — same validation, same lifecycle guards, same results. If you already use Agent access, your keys work here unchanged.