Create your integration
Open Account → Developer. Give the connection a name such as Example warehouse and choose only the key scopes your software needs.
VapeTax developer platform · API v1
Connect your warehouse, ERP or production tools to VapeTax. Read product and batch data, manage operational records, reserve exact units and follow every despatch through to its duty record.
The API is designed for your server to call VapeTax using a key created by your account owner. The same account boundaries and inventory protections apply to API operations and the app.
Open Account → Developer. Give the connection a name such as Example warehouse and choose only the key scopes your software needs.
Check the key’s effective operations, environment, request limits and release readiness before beginning a workflow.
Find the operation, required scope, request fields and responses below. Start with reads, then test writes in your approved isolated environment.
https://api.vapetax.co.uk/integrations/v1Use the API origin, not the website address. Your technical contact supplies the origin for an authorised sandbox. An example local origin is http://localhost:4016; localhost means the computer running your connector.
# Set VAPETAX_API_URL and VAPETAX_API_KEY in private server configuration.
# VAPETAX_API_URL contains the origin, without /integrations/v1.
curl "$VAPETAX_API_URL/integrations/v1/capabilities" \
-H "Authorization: Bearer $VAPETAX_API_KEY" \
-H "Accept: application/json"Availability is checked at runtime.
These pages describe the API contract. A deployed endpoint or an issued key does not enable live warehouse releases. Live release acceptance and live provider transmission remain disabled. Inventory onboarding, reservations, transfers and operational writes require the authorised isolated pilot and their own scopes.
Send your integration key in the Authorization header on every request. Keep it in your server’s secret configuration. Never put it in a URL, browser storage, client-side code, source control, screenshots or a shared Postman export.
Authorization: Bearer <your integration API key>
Accept: application/json
Content-Type: application/jsonKeys are shown once. They expire within 90 days and can be revoked immediately. Each integration allows two active keys so you can create a replacement, update your connector, verify it and revoke the old key without interrupting service.
Read and write scopes are separate. A write scope does not automatically grant a read scope. The endpoint reference states the exact access needed; GET /capabilities returns the effective scopes of the key you are using.
| Work you need to do | Access to grant | What to expect |
|---|---|---|
| Read or import products | products:read / products:write | Catalogue operations. Import alone creates no stock, release or duty. |
| Find inventory and stage holdings | inventory:read / inventory:write | Traceability reads, manifest staging and controlled transfers. Inventory approval remains with the owner. |
| Reserve specific units for an order | orders:read / orders:write | Read, create and cancel reservations. Reservations do not release goods or post duty. |
| Validate, record and track a release | releases:validate, releases:write, releases:read | Separate permissions for preview, atomic release/accounting and status reads. |
| Manage other operational records | Use the scope shown for that endpoint | Stock, production, purchasing, contacts, stamps and VPD each retain their domain permissions and evidence protections. |
The account owner creates keys and reviews inventory using their browser session. Integration keys cannot become an owner, approve opening holdings, manage credentials, access platform administration or restore the workspace. Owner-only endpoints are separately labelled in the reference.
Begin by synchronising the product catalogue. A product has a stable VapeTax ID, your external product ID, a name, SKU, optional liquid volume and packaging barcodes. Imported products appear in Products & Barcodes and the Production product picker.
Save the exact JSON payload and a stable batch key in your connector before sending. Send 1–100 products per import, with up to 20 barcodes per product.
POST /integrations/v1/products/import
Idempotency-Key: example-catalogue-001
Content-Type: application/json
{
"products": [
{
"externalProductId": "EXAMPLE-BLUE-10ML",
"name": "Example Blueberry 10ml",
"sku": "BLUE-10ML",
"volumeMl": 10,
"barcodes": [
{
"code": "EXAMPLE-BLUE-ITEM",
"level": "ITEM",
"unitsPerPack": 1
},
{
"code": "EXAMPLE-BLUE-PACK5",
"level": "PACK",
"containsCode": "EXAMPLE-BLUE-ITEM",
"containsQty": 5
}
]
}
]
}A first accepted import returns HTTP 201; an identical retry returns HTTP 200 and the original import ID. A changed payload using an accepted key returns a conflict. The import operation does not silently overwrite or adopt an existing catalogue product; resolve mapping conflicts with the account owner.
| Identifier | What it identifies | Example use |
|---|---|---|
| SKU / product barcode | A product or packaging configuration shared by many units. | Find the product and its pack size. |
| Stamp code | One specific retail unit. | Reserve or release that exact unit. |
| Aggregation code | One specific physical pack, case, master box or pallet. | Resolve the actual leaf units inside that container. |
Never multiply a packaging barcode’s nominal pack size to establish a duty quantity. Warehouse actions use the actual recorded units. A unique warehouse-readable aggregation alias must identify the physical container; a SKU barcode cannot serve as that alias.
Use inventory queries to connect the commercial product to the individual goods. Query by SKU, packaging barcode, batch, production number or verified warehouse. Follow the returned cursor to read every page.
GET /inventory/scans reads known scanned units, including goods that have not been reconciled. GET /inventory/units reads approved holdings with warehouse and reservation context. A scan record alone is not proof of approved inventory or release eligibility. Native loose stamps may have no recorded product, batch or expiry; those values remain unknown.
GET /integrations/v1/inventory/units?sku=BLUE-10ML
GET /integrations/v1/inventory/units?barcode=EXAMPLE-BLUE-ITEM
GET /integrations/v1/inventory/units?batch=EXAMPLE-BATCH-001
GET /integrations/v1/inventory/units?productionNumber=PROD-EXAMPLE
GET /integrations/v1/inventory/units?warehouseId=<verified-warehouse-id>URL-encode each filter value. Codes and stable external identities are case-sensitive. A product may occur in many batches; use the batch or production reference when you need one manufacturing run.
| Data | How to use it |
|---|---|
| SKU and barcode | Match your ERP item to the VapeTax product and its packaging. |
| Batch and production number | Locate the source run and the stamp codes linked to it. |
| Expiry date | Read the recorded source expiry. A missing date is unknown; do not calculate an assumed expiry. |
| Aggregation and contents | Inspect the parent/child structure and actual units before choosing a whole case or part of a batch. |
| Holding, reservation and release data | Check where goods are held and whether they are available for the intended operation. Query results do not reserve stock. |
Read aggregation records and their contents with the inventory aggregation endpoints. Quantity-only legacy cases must be reconciled into complete identities before they can enter the verified release workflow.
The following shows selected fields from an illustrative units response. Null means the value has not been recorded. Reservation references are disclosed only to the integration that owns them.
{
"id": "example-unit-id",
"stampCode": "UKVDSDRY/FAKEEXAMPLE01",
"product": {
"id": "example-product-id",
"name": "Example Blueberry 10ml",
"sku": "BLUE-10ML",
"liquidMlPerRetailUnit": "10.000"
},
"batch": "EXAMPLE-BATCH-001",
"expiryDate": "2027-10-15",
"productionNumber": "PROD-EXAMPLE",
"warehouseId": "example-warehouse-id",
"treatment": "NEW_RELEASE",
"availability": "AVAILABLE",
"orderAllocationId": null,
"externalOrderId": null,
"aggregation": null
}The versioned integration API exposes operational workflows with explicit scopes. Use the endpoint reference for the exact method, path and fields. Writes preserve the same source, lifecycle, stock and evidence checks as the app.
| Feature | What your connector can work with | Rules that remain in place |
|---|---|---|
| Products & barcodes | Catalogue records, packaging hierarchy and product ingredients. | Approved product and barcode evidence cannot be invalidated by a later edit. |
| Ingredient & consumable stock | Stock records, receipts and supported corrections. | Receipt and conversion operations retain their existing stock effects; quantities must use the recorded units. |
| Production & wastage | Production plans, batch numbers, expiry dates, actual output and reports. | Planned output does not establish verified inventory. Retained sources cannot be rewritten. |
| Purchasing & contacts | Purchase orders and customer/supplier records. | Purchase-order status alone does not receive stock. |
| Stamps & aggregation | Supported lifecycle records and aggregation operations. | Enrolled goods retain their reconciliation and lifecycle guards. Local scanning is not provider-confirmed activation. |
| VPD records | Calculator records and supported ordinary workflows. | Automatic release lines and protected prior-duty evidence cannot be edited or deleted through the API. |
Use workflow endpoints for physical goods.
Reserve through order allocations, move verified holdings through inventory transfers, and release into free circulation through the release API. A generic stock edit, status change or legacy stamp release cannot replace mandatory VPD accounting.
Access to a documented operation is still subject to account status, plan limits, the key’s scope and server readiness. A protected-history conflict means the record needs reconciliation and an authorised correction, not a different endpoint.
Machine-created stamp receipts begin in Product duty suspended. Use the dedicated lifecycle and release operations for later actions; a receipt status cannot bypass release accounting.
An allocation reserves specific retail units for an external order. It records an exact selection of stamp codes or aggregation contents, rather than only a planned quantity. Read the reservation to see its current state; cancel it through the cancellation endpoint when those goods are no longer required.
GET /integrations/v1/order-allocations
POST /integrations/v1/order-allocations
GET /integrations/v1/order-allocations/{id}
POST /integrations/v1/order-allocations/{id}/cancelPOST /integrations/v1/order-allocations
Idempotency-Key: example-allocation-001
{
"externalAllocationId": "EXAMPLE-ALLOCATION-001",
"externalOrderId": "EXAMPLE-ORDER-001",
"warehouseId": "example-warehouse-id",
"stampCodes": [
"UKVDSDRY/FAKEEXAMPLE01"
],
"aggregationCodes": []
}Allocation states are RESERVED, PARTIALLY_RELEASED, RELEASED and CANCELLED. The receipt reports total, reserved, released and cancelled units. New acceptance returns HTTP 201; an identical replay returns HTTP 200. A changed request under the same key or external allocation identity returns HTTP 409.
Two orders cannot reserve the same available unit at once. A release must respect the reservation’s order and integration ownership. Use a different stable despatch ID for each partial delivery of the same order. Cancelling a reservation does not reverse a completed release or its duty.
A transfer moves selected verified goods between approved premises in the same permitted duty-suspended state. It does not release them into free circulation, post VPD or claim SICPA confirmation. The service checks the exact units and current holding before recording the movement.
POST /integrations/v1/inventory/transfers{
"externalTransferId": "EXAMPLE-TRANSFER-001",
"fromWarehouseId": "example-source-warehouse-id",
"toWarehouseId": "example-destination-warehouse-id",
"movementType": "DUTY_SUSPENDED_TRANSFER",
"occurredAt": "2026-10-15T10:00:00+01:00",
"custodyEvidence": "FICTIONAL — replace with verified custody evidence",
"authorityEvidence": "FICTIONAL — replace with verified movement authority",
"stampCodes": [
"UKVDSDRY/FAKEEXAMPLE01"
],
"aggregationCodes": []
}Both warehouses must be verified and classified as DUTY_SUSPENDED by the owner. Cancel any reservation before transferring its units. Send a persisted Idempotency-Key: a new transfer returns HTTP 201 and an identical replay returns HTTP 200. The same key or external transfer identity with different content returns HTTP 409.
For a move into free circulation, use the release workflow below. Do not label it as a warehouse transfer to avoid accounting. Transfers involving a change of tax treatment require the reviewed workflow for that treatment.
Open the transfer or allocation endpoint below for its required JSON fields, idempotency rules and limits. Save the business reference and request before sending, then retain the returned operation ID with your warehouse record.
When goods actually enter free circulation, VapeTax records the local release and mandatory VPD accounting together in one transaction. If an acceptance check fails, neither a partial release nor a partial duty posting is left behind. Provider delivery happens separately.
The actual account owner approves the complete opening manifest, verified premises, source quantities, total liquid per retail unit, activation and historical duty evidence.
POST /release-validations checks eligibility and previews quantities and duty. Validation is advisory; it does not reserve goods or record a release.
POST /releases uses a stable externalDespatchId, your persisted Idempotency-Key, actual releasedAt time, warehouse, customer, operator and exact codes. Acceptance rechecks everything under the account lock.
HTTP 202 records local acceptance. Keep the returned release ID and poll the status URL. After a lost response, recover by externalDespatchId and retry the original request unchanged.
| Outcome | What it tells you | What to store |
|---|---|---|
| Local release | The exact selected units were recorded as released at the actual business time. | id, releaseStatus, releasedAt |
| VPD accounting | Duty was posted automatically or linked to verified prior duty. A posting does not mean payment to HMRC. | accounting.status, quantities, amounts and VPD references |
| SICPA notification | The independent delivery outcome, with its environment and event history. | notification, event IDs and environment |
NEW_RELEASE and RELEASE_FROM_BOND follow their verified accounting treatment; ALREADY_ACCOUNTED goods link to prior duty without charging again. The server resolves actual leaf units and liquid volume from the approved holding, never from a nominal case size or a client-supplied total.
A provider outage does not move the tax point. The local records and durable notification remain available for recovery. SIMULATED, LOCAL_ONLY, QUEUED and PROCESSING are not live SICPA confirmations. A SANDBOX confirmation proves only that sandbox event’s result. UNKNOWN requires investigation of the original event UUID; do not blindly resend it.
Accepted release evidence is retained. Corrections need a reviewed, authorised process; deleting ledger records or restoring an older workspace is not a reversal mechanism.
Persist the payload, business identity and Idempotency-Key before the first request. On a network timeout or retryable server failure, resend the identical request with the same key. Do not generate another despatch identity to resolve uncertainty. Key rotation does not change the integration’s business identity.
Only endpoints that document idempotency provide that guarantee. Do not automatically replay ordinary record-creation or stock-changing operations after a lost response; read the resulting state and reconcile first.
For cursor-paginated endpoints, start without cursor and pass the returned nextCursor on the next request. Stop when nextCursor is null. Keep the same filters across pages; do not interpret one page as the whole inventory.
GET /integrations/v1/products
GET /integrations/v1/products?cursor=<URL-encoded-nextCursor>Integration JSON bodies are limited to 1 MiB. The baseline throttle is 60 requests per key per minute and 300 per account per minute across integrations. Respect Retry-After on HTTP 429 and use bounded backoff with jitter. Read capabilities and each operation’s schema for batch, hierarchy and quantity limits.
The isolated release pilot currently accepts at most seven days of backdating and five minutes of future clock skew. Its rate and rounding policy is a review guard, not an agreement to enable live use. Preserve the actual timestamp and timezone offset in your saved request.
| HTTP status | Typical meaning | Next step |
|---|---|---|
| 400 | Invalid fields, date, code or selection. | Read message and any stable code. Correct the request before retrying. |
| 401 | Missing, expired, revoked or invalid key. | Check the configured secret and key expiry. Never fall back to an owner token. |
| 403 | Missing scope, disabled account or unavailable environment. | Check capabilities and ask the account owner or technical contact to review access. |
| 404 | The resource is unavailable to this account or integration. | Check the ID and its ownership. |
| 409 | Conflict, overlapping units, protected history or changed idempotent content. | Read the existing record and reconcile. A new key must not be used to bypass the conflict. |
| 413 / 415 | The body is too large or is not application/json. | Use the correct content type and the documented payload limits. |
| 429 / 5xx / timeout | Throttling, server failure or an uncertain response. | Use documented recovery and idempotency. Retain the original payload and identity. |
Your connector calls the standard VapeTax API. This does not install a native Sage or OrderWise plugin, remotely control scanners or automatically print aggregation aliases. Your technical contact confirms the installed versions, supported hooks and ownership of the adapter.
Sandbox acceptance is still a separate gate.
Local fixtures, simulation results and successful deployments are not real customer or SICPA sandbox evidence. Provider onboarding and the reviewed LOTE event requirements must be confirmed with the provider before live enablement.
Search by task, path, scope or field name. Expand an endpoint to see its parameters, nested JSON fields and response schemas. This reference is generated from the downloadable OpenAPI specification.
Paths below include /integrations/v1 for machine operations. Owner management paths use a browser session and are visible through the Access filter. This reference never asks you to paste a secret or sends API requests.
Matching endpoints: 123 of 142
/integrations/v1/capabilitiesRead supported operations and limits/integrations/v1/productsRead product catalogue with cursor pagination/integrations/v1/products/importImport new catalogue products atomically/integrations/v1/warehousesList verified warehouses/integrations/v1/inventory-manifestsStage a complete inventory manifest/integrations/v1/inventory-manifests/{id}Read inventory report and review status/integrations/v1/release-validationsPreview eligibility and duty without recording a release/integrations/v1/releasesList releases or recover by external despatch ID/integrations/v1/releasesRecord one local release and automatic VPD accounting/integrations/v1/releases/{id}Read local release, VPD and SICPA status/integrations/v1/contactsCreate — Contacts/integrations/v1/contactsContacts — Contacts/integrations/v1/contacts/{id}Update — Contacts/integrations/v1/contacts/{id}Remove — Contacts/integrations/v1/contacts/{id}Contact — Contacts/integrations/v1/inventory/unitsUnits — Inventory/integrations/v1/inventory/lotsLots — Inventory/integrations/v1/inventory/scansScans — Inventory/integrations/v1/inventory/aggregationsAggregations — Inventory/integrations/v1/inventory/transfersTransfer — Inventory/integrations/v1/inventory/transfersTransfers — Inventory/integrations/v1/inventory/transfers/{id}Transfer Detail — Inventory/integrations/v1/order-allocationsReserve — Orders/integrations/v1/order-allocationsAllocations — Orders/integrations/v1/order-allocations/{id}Allocation — Orders/integrations/v1/order-allocations/{id}/cancelCancel — Orders/integrations/v1/catalogueCatalogue — Products/integrations/v1/catalogueCreate — Products/integrations/v1/catalogue/{id}Product — Products/integrations/v1/catalogue/{id}Update — Products/integrations/v1/catalogue/{id}Remove — Products/integrations/v1/production-ordersProduction Orders — Production/integrations/v1/production-ordersCreate — Production/integrations/v1/production-orders/{id}Production — Production/integrations/v1/production-orders/{id}Update — Production/integrations/v1/production-orders/{id}Remove — Production/integrations/v1/purchase-ordersPurchase Orders — Purchases/integrations/v1/purchase-ordersCreate — Purchases/integrations/v1/purchase-orders/{id}Purchase — Purchases/integrations/v1/purchase-orders/{id}Update — Purchases/integrations/v1/purchase-orders/{id}Remove — Purchases/integrations/v1/stock/{resource}Stock — Stock/integrations/v1/stamp-ordersStamp Orders — Stamps/integrations/v1/manufacturersManufacturers — Stamps/integrations/v1/production-orders/importImport Many — Production/integrations/v1/production-orders/{id}/startStart — Production/integrations/v1/production-orders/{id}/completeComplete — Production/integrations/v1/production-orders/{id}/actualsSave Actuals — Production/integrations/v1/production-orders/{id}/invoiceUpload Invoice — Production/integrations/v1/production-orders/{id}/invoiceDownload Invoice — Production/integrations/v1/production-orders/{id}/invoiceDelete Invoice — Production/integrations/v1/catalogue/lookupLookup — Products/integrations/v1/catalogue/barcodes/{barcodeId}Update Barcode — Products/integrations/v1/catalogue/barcodes/{barcodeId}Remove Barcode — Products/integrations/v1/catalogue/aggregates/{aggregateId}Link Aggregate — Products/integrations/v1/catalogue/{id}/barcodesAdd Barcode — Products/integrations/v1/purchase-orders/{id}/statusSet Status — Purchases/integrations/v1/purchase-orders/{id}/invoiceUpload Invoice — Purchases/integrations/v1/purchase-orders/{id}/invoiceDownload Invoice — Purchases/integrations/v1/purchase-orders/{id}/invoiceDelete Invoice — Purchases/integrations/v1/stamps/scans/verifyVerify Scans — Stamps/integrations/v1/stamps/activateActivate Stamps — Stamps/integrations/v1/stamps/activate-packActivate And Pack — Stamps/integrations/v1/stamps/destroyDestroy Stamps — Stamps/integrations/v1/stamps/shipShip Stamps — Stamps/integrations/v1/stamps/receiveReceive Stamps — Stamps/integrations/v1/stamps/aggregatesCreate Aggregate — Stamps/integrations/v1/stamps/aggregatesList Aggregates — Stamps/integrations/v1/stamps/aggregates/masterCreate Master Aggregate — Stamps/integrations/v1/stamps/aggregates/{id}/stampsList Aggregate Stamps — Stamps/integrations/v1/stamps/aggregates/{id}/breakBreak Aggregate — Stamps/integrations/v1/stamps/manufacturersCreate Manufacturer — Stamps/integrations/v1/stamps/manufacturers/{id}Update Manufacturer — Stamps/integrations/v1/stamps/manufacturers/{id}Delete Manufacturer — Stamps/integrations/v1/stamps/agg-codes/importImport Agg Codes — Stamps/integrations/v1/stamps/agg-codesAgg Code Summary — Stamps/integrations/v1/stamps/case-labelsCase Labels — Stamps/integrations/v1/stamps/scan-photosSave Scan Photos — Stamps/integrations/v1/stamps/scan-photosList Scan Photos — Stamps/integrations/v1/stamps/scan-photos/{id}/fileGet Scan Photo — Stamps/integrations/v1/stampsCreate Order — Stamps/integrations/v1/stamps/{id}Update Order — Stamps/integrations/v1/stamps/{id}Delete Order — Stamps/integrations/v1/stock/pgvgAdd Pgvg — Stock/integrations/v1/stock/pgvg/{id}Edit Pgvg Log — Stock/integrations/v1/stock/pgvg/{id}Delete Pgvg Log — Stock/integrations/v1/stock/nicotineAdd Nicotine — Stock/integrations/v1/stock/nicotine/log/{id}Edit Nic Log — Stock/integrations/v1/stock/nicotine/log/{id}Delete Nic Log — Stock/integrations/v1/stock/nicotine/{id}Delete Nicotine Stock — Stock/integrations/v1/stock/nicotine/convertConvert Nicotine — Stock/integrations/v1/stock/sweetener/powderAdd Sweetener Powder — Stock/integrations/v1/stock/sweetener/liquidAdd Sweetener Liquid — Stock/integrations/v1/stock/sweetener/convertConvert Sweetener — Stock/integrations/v1/stock/sweetener/log/{id}Delete Sweetener Log — Stock/integrations/v1/stock/cbdAdd Cbd — Stock/integrations/v1/stock/cbd/convertConvert Cbd — Stock/integrations/v1/stock/cbd/log/{id}Delete Cbd Log — Stock/integrations/v1/stock/cbd/{id}Delete Cbd Stock — Stock/integrations/v1/stock/flavouringsCreate Flavouring — Stock/integrations/v1/stock/flavourings/{id}/addAdd Flavouring Stock — Stock/integrations/v1/stock/flavourings/{id}Update Flavouring — Stock/integrations/v1/stock/flavourings/{id}Delete Flavouring — Stock/integrations/v1/stock/flavourings/log/{id}Edit Flav Log — Stock/integrations/v1/stock/flavourings/log/{id}Delete Flav Log — Stock/integrations/v1/stock/labelsAdd Label — Stock/integrations/v1/stock/labels/{id}/addAdd Label Stock — Stock/integrations/v1/stock/labels/log/{id}Edit Lbl Log — Stock/integrations/v1/stock/labels/log/{id}Delete Lbl Log — Stock/integrations/v1/stock/labels/{id}Delete Label Sku — Stock/integrations/v1/stock/bottlesAdd Bottle — Stock/integrations/v1/stock/bottles/{id}/addAdd Bottle Stock — Stock/integrations/v1/stock/bottles/log/{id}Edit Bottle Log — Stock/integrations/v1/stock/bottles/log/{id}Delete Bottle Log — Stock/integrations/v1/stock/bottles/{id}Delete Bottle Size — Stock/integrations/v1/vpdList — Vpd/integrations/v1/vpd/production-ordersProduction Orders — Vpd/integrations/v1/vpd/import-ordersImport Orders — Vpd/integrations/v1/vpd/ordersCreate Order — Vpd/integrations/v1/vpd/orders/{id}Update Order — Vpd/integrations/v1/vpd/orders/{id}Remove Order — Vpd/integrations/v1/vpd/orders/{id}/entriesAdd Entry — Vpd/integrations/v1/vpd/entries/{id}Remove Entry — VpdImport the OpenAPI specification into your API tooling or use the Postman collection. The .NET examples show durable request identities, bounded retries and separate release/accounting/provider outcomes.