REST API guide

The public REST API is the read/write surface for external systems — custom ERPs, Zapier, in-house scripts — to consume and feed pricing data without touching the panel. This guide covers authentication and concepts; for the full endpoint-by-endpoint reference (request/response schemas, try-it-out), see Interactive reference docs below.

Base URL

All API routes are prefixed with /api on your instance's domain, e.g. https://your-instance.example.com/api.

Authentication

The API uses Laravel Sanctum personal access tokens (bearer tokens), one per integration/use case.

  1. Log into the panel, go to Settings → API Tokens.
  2. Enter a name for the token (e.g. "Zapier", "ERP sync") and click Create token.
  3. Copy the token immediately — it's shown once, in plaintext, and never again.
  4. Send it on every request:
bash
curl https://your-instance.example.com/api/products \
  -H "Authorization: Bearer <token>" \
  -H "Accept: application/json"

A token has full access to everything your account can see — there's no scoping below the account level. Revoke a token from the same Settings page if it's compromised or no longer needed; revocation is immediate.

Concepts

  • Everything is scoped to your account. A token only ever sees/modifies data belonging to the account it was created under — there's no cross-tenant access.
  • Pagination. List endpoints return Laravel's standard paginated shape (data, links, meta). Follow meta.current_page/links.next, or pass ?page=.
  • sell_price vs smart_price. sell_price is the live price. smart_price is what the currently-matching pricing rule computes — always kept fresh, but only becomes sell_price once an applied price change writes it. Read-only via the API; never accepted on create/update.

Common endpoints

Method & path What it does
GET /api/products List your products. Supports ?sort= (name, sell_price, smart_price, created_at, updated_at), ?dir=, ?q= (search name/SKU/GTIN), ?updated_since=, ?smart_price_since= (only products whose computed price changed).
POST /api/products Create a product.
GET /api/products/{id} Fetch one product, including which pricing rules currently apply to it.
PATCH /api/products/{id} Update a product.
DELETE /api/products/{id} Delete a product.
POST /api/products/import Bulk create/update by gtin. Each row is validated and upserted independently — one bad row doesn't fail the batch. See Bulk import below.
GET /api/products/export CSV snapshot of your catalog.
GET /api/products/{id}/competitor-urls List a product's tracked competitor URLs and latest prices.
POST /api/products/{id}/competitor-urls Track a new competitor URL for a product.
DELETE /api/products/{id}/competitor-urls/{urlId} Stop tracking a competitor URL.
POST /api/products/{id}/competitor-urls/{urlId}/scrape Trigger an on-demand scrape (async — price updates once the worker finishes, usually within seconds).
GET /api/products/{id}/price-history Competitor price time series for a product. ?since= filters by scrape date.
GET /api/price-changes Pricing rule suggestion/applied history. ?status= (pending/applied/rejected), ?since=.

Bulk import

POST /api/products/import accepts a products array; each row is matched/upserted by gtin:

bash
curl -X POST https://your-instance.example.com/api/products/import \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
      {"gtin": "0123456789012", "name": "Example Product", "sell_price": 29.99, "sku": "EX-001"}
    ]
  }'

Response reports per-row outcome so partial failures are visible, not silently swallowed:

json
{
  "created": 1,
  "updated": 0,
  "failed": 0,
  "results": [
    {"index": 0, "gtin": "0123456789012", "status": "created"}
  ]
}

Errors

Standard Laravel conventions: 401 for a missing/invalid token, 403 for accessing another account's resource, 404 for a resource that doesn't exist (or isn't yours — not distinguished, to avoid leaking existence), 422 with a Laravel-shaped errors object for validation failures.

Interactive reference docs

The full API reference — every endpoint, parameters, request/response schemas, and a browser "try it" console — is generated directly from the code (routes, form requests, models), so it can't drift out of sync with what's actually deployed.

  • Browse it: /docs/api on your instance.
  • Raw OpenAPI 3 spec: /docs/api.json — feed this into Postman, Insomnia, an SDK generator, or any OpenAPI-compatible tool.

To regenerate the spec as a file (e.g. for committing a snapshot or CI):

bash
php artisan scramble:export

Writes api.json to the api/ directory (gitignored — treat it as a build artifact, regenerate rather than hand-edit).

Public in every environment, same as the guides on this page — restrict it (e.g. to logged-in accounts) by changing the viewApiDocs gate in AppServiceProvider if you'd rather not expose your API shape to anonymous visitors.