Developers

The Dryline VMS API.

Connect your own systems to your machines, layouts, stock, products and sales.

Included in DrylineVMS ProREST + JSONOpenAPI 3.1

Quick start

Make a key and call the API.

  1. In the Dryline console, open Pro, then API access, and make a key. Owners and admins can make keys.
  2. Copy it when it is shown. It is shown once.
  3. Send it as Authorization: Bearer <key> to https://app.drylinevms.com/api/v1.
  4. For more rows, pass the answer’s nextCursor as cursor.

The full reference is the OpenAPI document, which most API tools can import.

curl https://app.drylinevms.com/api/v1/sales?limit=100 \
  -H "Authorization: Bearer dl_live_…"
{
  "data": [
    {
      "id": "6d1c…",
      "status": "completed",
      "tender": "card",
      "occurredAt": "2026-10-14T18:02:11.000Z",
      "machineId": "a3f0…",
      "slot": "B4",
      "productName": "Sparkling Water 12oz",
      "amountCents": 225,
      "taxCents": 17,
      "costCents": 61,
      "currency": "USD"
    }
  ],
  "nextCursor": "MjAyNi0xMC0xNFQx…"
}

Endpoints

What you can read.

GET /locationsYour locations and each one’s time zone
GET /machinesYour machines and what needs attention at each
GET /machines/{id}One machine
GET /machines/{id}/planogramThe published layout: slot, product, price, capacity, par level
GET /machines/{id}/dex-readingsDEX audit reads (add include=raw for the file itself)
GET /inventoryStock in every slot
GET /productsYour product catalog
GET /salesEvery item vended, with tax, cost and cash or card
GET /sales/summarySales totals for a period in your local time
GET /cash-collectionsCash taken out of machines
GET /routesDriver routes and their stops
GET /service-visitsMachines marked serviced
GET /warehouse/stockWarehouse stock per product

Changes

What a read and write key can change.

Stock changes need an Idempotency-Key header. Send the same change again with the same key and nothing is counted twice: the answer is 200 with applied: false.

POST /productsAdd a product. A barcode (upc) or SKU is required.
PATCH /products/{id}Change or archive a product (active: false)
POST /machines/{id}/inventory-entriesRestock, correct or count a slot
POST /warehouse/receiptsReceive stock into the warehouse
curl -X POST https://app.drylinevms.com/api/v1/machines/{id}/inventory-entries \
  -H "Authorization: Bearer dl_live_…" \
  -H "Idempotency-Key: po-1001-a1" \
  -H "Content-Type: application/json" \
  -d '{"slot":"A1","type":"restock","units":6,"bestBy":"2027-01-31"}'

Webhooks

Told when it happens.

Add an https address under API access and pick its events. Each event is sent within about a minute as { id, type, createdAt, data }, where data is the same record the API returns. If your side is down it is sent again for about two days. The same event can arrive twice: use its id.

sale.completedA vend whose payment settled
sale.reversedA sale voided or refunded
dex.receivedA machine sent its DEX audit file
cash_collection.createdCash taken out of a machine
service_visit.createdA driver marked a machine serviced

Every message carries a Dryline-Signature header. Check it with the signing secret shown when you added the address:

import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody: the request body exactly as received, before JSON.parse.
function isFromDryline(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1 ?? '', 'hex');
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Keys, limits and formats

Keys

A key is either read only or read and write. Make one per system you connect, so one can be revoked without the others. Up to 10 at a time.

Limits

120 requests a minute per key, and 300 per account across its keys. Each answer carries X-RateLimit headers.

Money and time

Money is in whole cents of US dollars. Times are ISO-8601 in UTC. A value we do not know is null.

Pages

Long lists come in pages of up to 200 rows, in the order Dryline recorded them, so a late sale is never skipped.

Sales that change

A sale can settle, or be voided or refunded, after it is recorded. Ask /sales with updatedSince to pick those changes up.

Versions

Version 1 only gains fields. Ignore fields you do not know.

Errors

Every error has a code.

Errors are RFC 9457 problem details: a status, a code to branch on, a sentence, and the requestId.

missing_api_key401No Authorization header with a key.
invalid_api_key401The key is not valid. It may have been revoked.
wrong_environment401A test key was sent to production, or the other way round.
insufficient_scope403A read only key was used to change data.
plan_required403The account is not on DrylineVMS Pro.
account_suspended403The account is suspended.
account_canceled403The account is canceled.
vending_not_enabled403Vending is not turned on for the account.
wrong_host403The key was sent to another business’s address. Use app.drylinevms.com.
not_found404No such record for this business.
already_exists409A product with that barcode or SKU exists. existingId is its id.
invalid_request400A parameter is not valid. The errors list names each one.
rate_limited429Over the limit. Wait the Retry-After seconds.
internal_error500Something went wrong on our side. Quote the requestId.

Building an integration?

Tell us what you are connecting and we will help you map it.