Skip to content
ArtworkDesk Help
All help topics

Settings and account

API and webhooks

Connect your own systems, Zapier or Make: read and add orders and customers with the API, and get told when things happen with webhooks.

The API lets your own systems, or tools like Zapier and Make, read your orders, customers, quotes and products, and add orders and customers. Webhooks work the other way: ArtworkDesk sends a message to an address of yours when something happens, such as an order being dispatched.

The API and webhooks are part of the Business plan. They aren't included in the free trial, which has the Pro plan's features.

Open Settings > API and webhooks. You need the Settings and team permission (Owners and Admins have it).

API keys

Every request to the API needs a key. Make a separate key for each system that uses it, so you can remove one without affecting the others.

  1. Under API keys, enter what it's for in New key for, for example "Zapier" or "Our website".
  2. Tick Can add orders and customers if it needs to add things. Leave it unticked for a key that can only read.
  3. Click Make a key.
  4. Click Copy and paste the key somewhere safe straight away. It starts with awd_.

Each key is listed with its name, the first few characters of the key, whether it Reads only or Reads, and adds orders and customers, and when it was last used.

To remove a key, click Remove next to it and confirm. Anything using it stops working straight away.

Webhooks

  1. Under Webhooks, paste the address to send to in Send to. It must start with https://.
  2. Under When, tick the events you want. An order (or part of it) is dispatched is ticked to start with.
  3. Click Add webhook.
  4. Click Send a test to check the address answers.

Each webhook shows its address, the events it's for and when it last sent something, with:

  • Working: the last message went through.
  • Failing: the last message didn't go through. The reason is shown underneath.
  • Off: it isn't sending.

Click Edit to change the events, untick or tick Sending, see the Signing secret (click Show), or Remove the webhook. When editing, you can also tick Every event, which includes any events added in future. Click Save when you're done.

If an address fails 50 times in a row, the webhook is switched off and says so. Fix the problem, then tick Sending and click Save to switch it back on.

The basics

The rest of this article is for whoever sets up the connection.

The base address is your ArtworkDesk address followed by /api/v1:

https://yourbusiness.artworkdesk.com/api/v1

The exact address is shown under Using the API on the API and webhooks page. If your own domain is live, you can use that instead.

Send your key as a bearer token, and ask for JSON, on every request:

GET https://yourbusiness.artworkdesk.com/api/v1/orders
Authorization: Bearer awd_...
Accept: application/json

Send Content-Type: application/json with a JSON body when adding things. Everything that comes back is JSON.

Endpoints

Method and path What it does Key needed
GET /me Your business name, its ArtworkDesk address, your plan and currency. Handy for checking a key works. Any
GET /orders Orders, newest first. Any
GET /orders/{number} One order, by job number (such as JOB-1001) or its id. Any
POST /orders Add an order. Can add
GET /customers Customers, newest first. Any
GET /customers/{id} One customer, by id. Any
POST /customers Add a customer. Can add
GET /quotes Quotes, newest first. Any
GET /quotes/{number} One quote, by quote number (such as Q-1001) or its id. Any
GET /products Products that are in use, by name. Any

Filtering lists

Endpoint Query parameters
GET /orders status: orders with at least one product line at this status (see below). reference: the website or other system's order reference. updated_since: orders changed since this date or time.
GET /customers email: the customer with this email address (any capitals). updated_since.
GET /quotes status: draft, sent, accepted, declined, expired or approved. updated_since.
GET /products None apart from paging.

Use an ISO 8601 date or date and time for updated_since, for example 2026-10-01 or 2026-10-01T09:00:00Z.

Product line statuses:

status Shown in ArtworkDesk as
awaiting_artwork Awaiting artwork
artwork_received Mockup to do
file_check File to check
proof_sent Awaiting approval
changes_requested Changes requested
approved Queued for production
in_production In production
ready Ready to dispatch
dispatched Dispatched
cancelled Cancelled

An order's own status is the least advanced status of its lines that aren't cancelled. See What each status means.

Paging

Lists come 50 at a time. Add per_page (1 to 100) and page to change this. Each list looks like this:

{
  "data": [ ... ],
  "meta": {
    "page": 1,
    "per_page": 50,
    "total": 132,
    "next": "https://yourbusiness.artworkdesk.com/api/v1/orders?page=2"
  }
}

next is the address of the next page, or null on the last page. A single order, customer or quote comes back as {"data": { ... }}.

Adding an order

POST /orders with a JSON body:

{
  "reference": "WEB-5521",
  "customer": {
    "email": "[email protected]",
    "name": "Sam Patel",
    "company": "Riverside FC",
    "phone": "07700 900123"
  },
  "delivery_address": "12 High Street\nHarlow\nCM20 1AA",
  "delivery_method": "Royal Mail Tracked 48",
  "due_date": "2026-11-20",
  "rush": false,
  "request_artwork": true,
  "items": [
    { "product_code": "TS100", "colour": "Navy", "size": "M", "quantity": 10, "decoration": "Screen print", "print_positions": "Front" },
    { "product_code": "TS100", "colour": "Navy", "size": "L", "quantity": 15, "decoration": "Screen print", "print_positions": "Front" }
  ]
}
Field Rules What it does
reference Optional, up to 120 characters. Your own order number. If an order added through the API already has this reference, that order comes back instead of a new one being made.
customer.email Required. The customer is found by email address (any capitals), or added if they're new. An existing customer's details aren't changed.
customer.name Required, up to 150. Used when the customer is added.
customer.company Optional, up to 150.
customer.phone Optional, up to 40.
delivery_address Optional, up to 2000. Use \n between lines. Left out, the customer's delivery address is used, or their billing address.
delivery_method Optional, up to 120.
notes Optional, up to 5000. Shown to the customer at the top of their order page, as Message shown to the customer. Don't put anything private here.
due_date Optional date. Left out, it's worked out from your lead times, as for any new order.
rush Optional, true or false. Marks it as a rush order.
request_artwork Optional, true or false. true emails the customer asking for their design, if any line is waiting for artwork.
items Required, 1 to 100 items. The products.
items[].product_code Optional, up to 80. Matched against your product codes (any capitals). A code that starts with one of yours followed by a hyphen, such as TS100-NAVY-M, matches TS100.
items[].description Required if there's no product_code, up to 255. Matched against your product names when there's no code, or the code doesn't match. Otherwise it becomes the line's description.
items[].quantity Required, 1 to 1,000,000.
items[].colour Optional, up to 80.
items[].size Optional, up to 20. Such as M.
items[].decoration Optional, up to 120. Left out, the product's usual decoration is used.
items[].print_positions Optional, up to 255.
items[].instructions Optional, up to 3000. Instructions for the team.
items[].needs_mockup Optional, true or false. Left out, the product's or your usual setting is used.

Items for the same product, colour, decoration, print positions and instructions are put on one line, with their sizes. So send one item for each size, as in the example, and you get one line of 25 with 10 M and 15 L.

The order's Came from is Another system (API), and your team gets the usual "new order from your website" notification.

It answers 201 with the new order, or 200 with the existing one (and a message) if the reference was already used.

Adding a customer

POST /customers with a JSON body:

{
  "name": "Sam Patel",
  "company": "Riverside FC",
  "email": "[email protected]",
  "phone": "07700 900123",
  "billing_address": "12 High Street\nHarlow\nCM20 1AA",
  "delivery_address": null,
  "notes": "Club secretary"
}

name (up to 150) and email are required. company (150), phone (40), billing_address, delivery_address (2000 each) and notes (5000) are optional.

It answers 201 with the new customer. If a customer with that email address already exists, it answers 200 with that customer and a message, and doesn't change them.

Errors

Code Means
401 No key, or the key isn't right.
403 The account isn't active, your plan doesn't include the API, or a read-only key tried to add something.
404 That order, quote or customer doesn't exist.
422 Something in the body isn't right. errors says which field and why.
429 Too many requests. Wait and try again.

Errors come back with a message:

{
  "message": "The customer.email field is required.",
  "errors": { "customer.email": ["The customer.email field is required."] }
}

Rate limit

You can make up to 120 requests a minute. Over that, you get 429 until the minute is up.

What's sent back

The API and webhooks use the same shapes.

Order

{
  "id": 4182,
  "number": "JOB-1001",
  "reference": "WEB-5521",
  "source": "api",
  "status": "awaiting_artwork",
  "status_label": "Awaiting artwork",
  "rush": false,
  "due_date": "2026-11-20",
  "delivery_method": "Royal Mail Tracked 48",
  "delivery_address": "12 High Street\nHarlow\nCM20 1AA",
  "customer": { ... },
  "items": [ ... ],
  "customer_link": "https://yourbusiness.artworkdesk.com/o/...",
  "created_at": "2026-10-25T10:15:00+00:00",
  "updated_at": "2026-10-25T10:15:00+00:00"
}

customer_link is the customer's own order page. source is where the order came from, such as api, shopify, woocommerce, etsy or quote.

Product line (item)

{
  "id": 9921,
  "description": "Heavyweight T-shirt",
  "product_code": "TS100",
  "quantity": 25,
  "colour": "Navy",
  "sizes": { "M": 10, "L": 15 },
  "decoration": "Screen print",
  "print_positions": "Front",
  "instructions": null,
  "status": "awaiting_artwork",
  "status_label": "Awaiting artwork",
  "department": null,
  "stage": null
}

sizes is null when the line has no size breakdown. department and stage are filled in once the line is in production.

Customer

{
  "id": 311,
  "name": "Sam Patel",
  "company": "Riverside FC",
  "email": "[email protected]",
  "phone": "07700 900123",
  "billing_address": "12 High Street\nHarlow\nCM20 1AA",
  "delivery_address": null,
  "custom": [ { "label": "Account number", "type": "text", "value": "RFC-01" } ],
  "created_at": "2026-10-25T10:15:00+00:00"
}

custom holds your own custom fields that have a value. email is null for customers added without one, for example from a shop order with no email.

Quote

{
  "id": 77,
  "number": "Q-1001",
  "title": "Quote request",
  "status": "draft",
  "status_label": "Draft",
  "subtotal": 250.0,
  "vat": 50.0,
  "total": 300.0,
  "valid_until": "2026-11-24",
  "customer": { ... },
  "items": [
    { "description": "Heavyweight T-shirt", "kind": "product", "quantity": 25, "unit_price": 8.5, "discount_percent": 0, "vat_rate": 20 },
    { "description": "Setup: Heavyweight T-shirt", "kind": "charge", "quantity": 1, "unit_price": 37.5, "discount_percent": 0, "vat_rate": 20 }
  ],
  "order_id": null,
  "created_at": "2026-10-25T10:15:00+00:00",
  "updated_at": "2026-10-25T10:15:00+00:00"
}

order_id is the order made from the quote, once there is one.

Product

{ "id": 12, "name": "Heavyweight T-shirt", "product_code": "TS100", "category": "Clothing", "decoration": "Screen print", "lead_days": 7, "needs_mockup": true }

Webhook messages

Each webhook is a POST with a JSON body like this:

{
  "id": "6f1c3a52-8f0e-4a8e-9a7d-2b1f0c9e4d11",
  "event": "order.dispatched",
  "created_at": "2026-10-25T15:42:10+00:00",
  "business": { "name": "Northside Print", "slug": "northside" },
  "data": { ... }
}

id is different for every message, so you can spot one you've already handled.

event When data holds
order.created An order is added, however it's added. order
item.approved A product line is approved for production. order_number, item, order
item.ready A product line is ready to dispatch. order_number, item, order
order.dispatched An order, or part of it, is dispatched. order, and shipment with carrier, tracking_number, tracking_link, shipped_on and items (the ids of the lines sent)
quote.accepted A customer accepts a quote. quote
quote.approved A quote is approved. quote
quote.requested A quote request arrives from your website. See Quote requests from your website. quote
order.reorder_requested A customer asks to order again from their order page. order_number, customer, lines (each with item_id, description, quantity and sizes) and note
customer.created A customer is added, however they're added. customer
test You click Send a test. message

Headers

Header What it holds
Content-Type application/json
X-ArtworkDesk-Event The event, such as order.dispatched.
X-ArtworkDesk-Signature sha256= followed by the HMAC-SHA256 of the body, using the webhook's signing secret.

Answer with any 2xx code within 8 seconds. Anything else, or no answer, counts as a failure.

Checking the signature

Work out the HMAC-SHA256 of the raw body, exactly as it arrived, with your signing secret (it starts with whsec_). Compare it with the header, and ignore the message if they don't match.

PHP:

$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, getenv('ARTWORKDESK_SECRET'));
$given = $_SERVER['HTTP_X_ARTWORKDESK_SIGNATURE'] ?? '';

if (! hash_equals($expected, $given)) {
    http_response_code(401);
    exit;
}

$message = json_decode($body, true);
// $message['event'], $message['data'] ...
http_response_code(200);

Node (Express):

const crypto = require('crypto');

app.post('/artworkdesk', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.ARTWORKDESK_SECRET)
    .update(req.body)
    .digest('hex');
  const given = req.get('X-ArtworkDesk-Signature') || '';

  if (given.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
    return res.sendStatus(401);
  }

  const message = JSON.parse(req.body);
  // message.event, message.data ...
  res.sendStatus(200);
});

Using Zapier

When something happens in ArtworkDesk

  1. In Zapier, make a Zap and choose Webhooks by Zapier as the trigger, with the event Catch Hook.
  2. Copy the webhook address Zapier gives you.
  3. In ArtworkDesk, paste it into Send to under Webhooks, tick the events you want and click Add webhook.
  4. Make the event happen once, for example add a test customer for customer.created, so Zapier can see a real message with all its fields. Send a test only checks the connection: its message has no order or customer in it.
  5. Carry on with the Zap's actions in Zapier.

Adding orders or customers from Zapier

  1. In ArtworkDesk, make a key with Can add orders and customers ticked.
  2. In Zapier, add a Webhooks by Zapier action with the event Custom Request.
  3. Set the method to POST and the address to your base address followed by /orders or /customers.
  4. In Data, write the JSON body, as in the examples above, using fields from earlier steps.
  5. Add the headers Authorization (with Bearer and your key), Content-Type (application/json) and Accept (application/json).

Make and other tools work the same way: a webhook address to receive events, and an HTTP request with your key to add things.

Still need help? Email [email protected] with your business's address and what you were trying to do.