Trade accounts

Send us orders from your own system

REST over JSON. Your software posts an order, we digitize it, and the finished machine files come back to you automatically. One order can carry many logos, and each logo is charged at the rate already agreed on your account.

The API is for trade accounts on an agreed fixed price. If your account is quoted per job there is no rate for it to charge, and it will refuse the key. Read about trade accounts or ask us to set one up.

Start here

Four things to know first

Read these and the rest of the page will make sense.

  1. A line is one logo, and the logo is what is charged. An order groups lines for your own convenience: a purchase order, a batch, one of your customers. The order total is the sum of its lines.
  2. Your account manager issues the key. One key per integration, so one can be revoked without stopping the others. It is shown once and never again: we keep only its hash.
  3. A trade order starts straight away. No quote to approve and no payment to clear. It goes on your monthly invoice.
  4. Send a reference and retrying is safe. Resending the same reference returns the order that already exists rather than opening a second one, so a timeout cannot charge you twice.
Base URL https://api.plixalabs.com/api/trade/v1 Every request Authorization: Bearer plx_live_...

Endpoints

What you can call

Six of them. That is the whole surface.

  • GET /ping
    Check the key Answers with the account name, its currency and its payment terms. Use it once when you wire things up.
  • GET /services
    Prices and allowed values Every service, complexity and rush value we accept, with your own agreed price for each one, so you can price on your side and reconcile against the invoice.
  • POST /orders
    Place an order One order holding one or more lines. A line is one logo, and the logo is what is charged.
  • GET /orders
    List your orders Newest first, paged. Pass per_page up to 100.
  • GET /orders/{reference}
    One order By our order number or by the reference you sent with it, whichever you kept.
  • GET /orders/{reference}/files/{id}
    Download a finished file The files we produced. Your own artwork is not handed back, because that would look like a delivery and is not one.

Placing an order

Post the order, get the lines back priced

Sizes are millimetres. There is no unit switch: a form has one because a person thinks in inches, and offering it to a machine only creates a way for two systems to disagree about what 60 meant.

Request
curl -X POST https://api.plixalabs.com/api/trade/v1/orders \
  -H "Authorization: Bearer plx_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "PO-4471",
    "rush": "standard",
    "lines": [
      {
        "reference": "PO-4471-1",
        "design_name": "Lion crest",
        "service": "logo",
        "complexity": "medium",
        "placement": "Left chest",
        "width_mm": 60,
        "height_mm": 45,
        "machine_format": "dst",
        "artwork_urls": ["https://files.your-system.example/lion.ai"]
      },
      {
        "reference": "PO-4471-2",
        "design_name": "Sleeve badge",
        "service": "logo",
        "complexity": "simple",
        "artwork_urls": ["https://files.your-system.example/badge.png"]
      }
    ]
  }'
Response, 201
{
  "data": {
    "id": "PLX-26-00412",
    "reference": "PO-4471",
    "status": "order_received",
    "currency": "USD",
    "total": 16.0,
    "lines": [
      { "id": "PLX-26-00412-1", "reference": "PO-4471-1", "price": 8.0, "files": [] },
      { "id": "PLX-26-00412-2", "reference": "PO-4471-2", "price": 8.0, "files": [] }
    ]
  }
}

What a line may say

FieldNeededWhat it is
design_name required What to call the design. It is what your team and ours will both see.
service required One of the service keys from GET /services.
complexity required simple, medium or complex.
rush optional standard, rush or super_rush. Defaults to the order, then to standard.
reference optional Your own identifier for this line.
placement optional Left chest, cap front, jacket back, and so on.
garment optional What it is going on.
width_mm optional Finished width in millimetres.
height_mm optional Finished height in millimetres.
colors optional How many thread colours.
fabric optional Piqué, fleece, softshell, and so on.
backing optional Cut-away, tear-away, none.
machine_format optional The format you want back: dst, pes, jef and the rest.
instructions optional Anything a digitizer needs to know.
artwork one of two File parts, when you post the request as multipart/form-data.
artwork_urls one of two https URLs we fetch instead. A line needs artwork one way or the other.

Artwork, two ways

Either per line, and you can mix them across lines in one order. artwork_urls are https URLs we fetch: we do not follow redirects, so send the final URL, and the host has to resolve to a public address. Or post the whole request as multipart/form-data and send file parts as lines[0][artwork][]. Up to ten per line, 25 MB each. A line with neither is refused.

Getting work back

We call you when a logo is finished

Give us a callback URL and we post to it as each logo is done, and again when the whole order is. No callback is a supported choice: poll GET /orders instead.

What we send
POST https://your-system.example/hooks/plixalabs
X-PlixaLabs-Event: line.completed
X-PlixaLabs-Delivery: 1234
X-PlixaLabs-Signature: t=1761345600,v1=9f8a...

{
  "event": "line.completed",
  "sent_at": "2026-10-24T09:15:00+00:00",
  "order": { "id": "PLX-26-00412", "...": "..." },
  "line": {
    "id": "PLX-26-00412-1",
    "design_name": "Lion crest",
    "stitch_count": 7412,
    "files": [
      { "id": 88, "name": "lion-crest.dst", "kind": "dst", "url": "https://api.plixalabs.com/api/trade/v1/orders/PLX-26-00412/files/88" }
    ]
  }
}
Check the signature first
// Node. Verify before you trust the body.
const [t, v1] = req.headers['x-plixalabs-signature']
  .split(',')
  .map((part) => part.split('=')[1]);

const expected = crypto
  .createHmac('sha256', PLIXALABS_SIGNING_SECRET)
  .update(t + '.' + rawBody)   // the raw bytes, not JSON.stringify(req.body)
  .digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) return res.sendStatus(400);
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.sendStatus(400);
  • SignatureHMAC-SHA256 of t + "." + raw body, hex, using your signing secret. Compute it over the bytes you received, never over a re-encoded copy of the parsed JSON.
  • ReplayThe timestamp is inside the signed string. Reject anything whose t is more than a few minutes from now.
  • RetriesAnswer 2xx. Anything else is retried four times over about ten minutes, then we stop and the order is still readable by polling.
  • Exactly onceEach event is sent once per order. A retry on our side never produces a second call, so you do not need to de-duplicate, though checking the delivery id costs nothing.

When it says no

Errors

Every refusal is JSON with a message. These are the ones worth handling by code.

StatusWhat it means
401The key is wrong, or it has been revoked.
403The key is good, but the account is not on an agreed contract price. This API places orders at a rate already agreed, so there is nothing for it to charge. Talk to your account manager.
422Something in the order is not acceptable. The body names the field and says why.
404No such order on this account. Orders are scoped to the key, so another customer's number reads as missing rather than forbidden.
429Too many requests. 120 a minute, of which 60 may be new orders.

Ready to connect?

Ask your account manager for a key, or tell us what you are building and we will set the account up with it.

Upload artwork Call
WhatsApp