Skip to main content

Stock Integration Guide

This guide is aimed at external technical teams that need to integrate their systems with TicTAP's stock module via API and webhooks.


Table of contents

  1. Overview
  2. Authentication
  3. Domain concepts
  4. API — Product catalogue
  5. API — Stores and inventory
  6. API — Stock transactions
  7. API — Reports and export
  8. API — Webhooks
  9. Formats and conventions

Overview

The Core stock API is mounted under:

https://<host>/api/stock/

This surface provides backend endpoints for:

  • product catalogue reads and writes
  • store and store-node queries
  • stock item writes
  • stock transaction queries
  • transaction statistics
  • stock exports
  • stock webhook registration

This document intentionally covers only Core endpoints under /api/stock/*.


Authentication

Mechanism

The current authenticator accepts:

Authorization: <api_key>

or:

accessToken: <api_key>

It also accepts:

GET ...?accessToken=<api_key>

Typical headers

Authorization: <api_key>
Content-Type: application/json
Accept: application/json

Domain concepts

Operating modes

The stock component can operate in two modes:

ModeDescription
catalogProducts are managed in the dedicated stock catalogue. Product CRUD and bulk import are supported.
entitiesProducts are resolved through dynamic entities. Product reads may include values.

Store model

The Core API distinguishes between:

  • Store: configured store records returned by /api/stock/stores
  • StoreNode: nodes in the store hierarchy, used in summaries, children, replenish views, and transactions

Movement types

TypeDescription
inputGoods received
outputGoods consumed or dispatched
transferMove between nodes
adjustManual adjustment

Currency

Amounts are serialized as integers in the smallest currency unit (e.g. 9900 = 99.00 EUR). Currency is specified as an ISO 4217 code (EUR, USD, etc.). The team's configured currency is used by default. Product amounts are serialized as:

{ "amount": "1299", "currency": "EUR" }

API — Product catalogue

List products

GET /api/stock/products

Query parameters:

ParameterTypeDescription
namestringFilter by name
categoryIdstringFilter by category
querystringSearch by name or part number
ownerIdstringOwner enterprise ID

ownerId must match the authenticated enterprise in the current controller.

Response 200:

[
{
"id": "01HXYZ...",
"name": "HDMI Cable 2m",
"description": "Standard 4K HDMI cable",
"image": { "id": null, "url": null },
"image_url": null,
"qr_codes": ["QR-001"],
"unit_cost": { "amount": "1299", "currency": "EUR" },
"part_number": "CAB-HDMI-2M",
"category_id": "category-uuid",
"manufacturer_id": "manufacturer-uuid"
}
]

Get a product

GET /api/stock/products/{id}

In entities mode the payload may also include a values object.

Create a product

POST /api/stock/products
Content-Type: application/json

Body:

{
"name": "HDMI Cable 2m",
"unitCostAmount": 1299,
"unitCostCurrency": "EUR",
"description": "Standard 4K HDMI cable",
"partNumber": "CAB-HDMI-2M",
"qrCodes": ["QR-001"],
"categoryId": "category-uuid",
"manufacturerId": "manufacturer-uuid"
}
FieldRequiredDescription
nameYesProduct name
unitCostAmountYesUnit cost amount
unitCostCurrencyNoCurrency code. Defaults to EUR in the current controller if omitted
descriptionNoDescription
partNumberNoSKU / reference
qrCodesNoProduct QR codes
categoryIdNoCategory ID
manufacturerIdNoManufacturer ID
imageNoImage URL
idNoUUID or ULID. Auto-generated if omitted

Response 201:

{ "message": "Product created successfully" }

Update a product

PATCH /api/stock/products/{id}
Content-Type: application/json

Accepts any subset of the writable product fields.

Response 200: serialized product object.

Delete a product

DELETE /api/stock/products/{id}

Only available in catalog mode.

Response 204

Bulk product import

POST /api/stock/products/import
Content-Type: application/json

Performs an upsert keyed on partNumber.

Body:

{
"products": [
{
"partNumber": "CAB-HDMI-2M",
"name": "HDMI Cable 2m",
"unitCost": "1299",
"description": "Standard 4K HDMI cable",
"manufacturerName": "Brand X",
"categoryName": "Cables"
}
]
}
FieldRequiredDescription
partNumberYesUpsert key
nameYesProduct name
unitCostYesUnit cost amount
descriptionNoDescription
manufacturerNameNoManufacturer name. Created automatically if missing
categoryNameNoCategory name. Created automatically if missing
imageNoImage URL
idNoExplicit ID used on create when no existing product matches the partNumber

Response 200:

{
"message": "Products imported successfully",
"created": 5,
"updated": 12,
"processed": 17
}

API — Stores and inventory

List stores

GET /api/stock/stores

Returns configured Store records for the current team.

Optional query parameters:

ParameterTypeDescription
scopestringteam or enterprise

This endpoint does not enumerate the entire node tree.

Get store node summary

GET /api/stock/stores/{id}

Returns the summary of the requested node.

Get children of a store node

GET /api/stock/stores/{id}/children?page=1&limit=25

Returns paginated direct child nodes.

Get summaries for multiple store nodes

GET /api/stock/stores/summaries?ids[]=id1&ids[]=id2&ids[]=id3

Returns the summary for each requested node.

Available products in a store

GET /api/stock/stores/{id}/available-products

Alias:

GET /api/stock/stores/{id}/products

Response 200:

[
{
"id": "stock-item-uuid",
"name": "HDMI Cable 2m",
"description": "...",
"quantity": 42,
"min_quantity": 10,
"max_quantity": 100,
"qr_codes": ["QR-001"],
"image_url": null,
"image": { "id": null, "url": null },
"node_id": "store-node-uuid",
"unit_cost": { "amount": "1299", "currency": "EUR" },
"part_number": "CAB-HDMI-2M"
}
]

Replenishment view

GET /api/stock/stores/{id}/replenish.{format}

Supported route formats:

  • json
  • csv
  • xlsx

Returns stockable reads in the subtree that need replenishment.

The current controller returns a CSV response path for non-JSON exports, even for the xlsx suffix.

Create a stock item in a store

POST /api/stock/stores/{id}/items
Content-Type: application/json

Body:

{
"productId": "product-uuid",
"quantity": 0,
"minQuantity": 10,
"maxQuantity": 100
}

Required fields:

  • productId
  • quantity
  • minQuantity
  • maxQuantity

Response 201: serialized dynamic entity detail for the created stock item.

Update a stock item

PATCH /api/stock/items/{id}
Content-Type: application/json

Required fields in the current implementation:

  • quantity
  • minQuantity
  • maxQuantity

Response 200: serialized dynamic entity detail for the updated stock item.

Recalculate store summaries

POST /api/stock/stores/{id}/recalculate-summaries

Recalculates the summary for the store and descendants.


API — Stock transactions

Query transactions

GET /api/stock/transactions.{format}

Supported route formats:

  • json
  • csv
  • xlsx

Query parameters:

ParameterTypeDescription
from_datedatetime Y-m-d H:i:sStart date
to_datedatetime Y-m-d H:i:sEnd date
product_idstringProduct ID
origin_storestringOrigin store ID
destination_storestringDestination store ID
origin_nodestringOrigin node ID
destination_nodestringDestination node ID
quantitynumberQuantity filter
order_numberstringPartial match
reasonstringPartial match
user_idstringCreator user ID
movement_typestringinput, output, transfer, adjust
pageintegerPage number
limitintegerPage size

At least one of the following is required:

  • origin_store
  • destination_store

Response 200 in json:

{
"results": [
{
"id": "01HXYZ...",
"date_time": "2026-04-24T10:30:00+00:00",
"comments": "Delivery note 123",
"reason": "Monthly purchase",
"order_number": "ORD-2026-001",
"user_id": "user-uuid",
"movement": {
"type": "input"
},
"lines": [
{
"product_id": "product-uuid",
"quantity": 10.0
}
]
}
],
"total": 85,
"count": 25,
"pages": 4,
"page": 1,
"limit": 25
}

The current controller returns a CSV response path for non-JSON exports, even for the xlsx suffix.

Transaction summary

GET /api/stock/transactions/summary?store=<store_id>&from=<datetime>&to=<datetime>

Required query parameters:

  • store
  • from
  • to

Returns aggregate transaction statistics for a single store.

Daily transaction stats

GET /api/stock/transactions/daily-stats?stores[]=id1&stores[]=id2&from=<datetime>&to=<datetime>

Required query parameters:

  • stores[]
  • from
  • to

Returns daily aggregate statistics for one or more stores.


API — Reports and export

Store inventory report

GET /api/stock/stores/{id}/report.{format}

Supported route formats:

  • csv
  • xlsx

Optional query parameter:

ParameterDescription
currencyCurrency used for report generation

Returns a consolidated store inventory export.

The current controller returns a CSVResponse even for the xlsx suffix.

Team inventory report

GET /api/stock/team/report.{format}

Supported route formats:

  • csv
  • xlsx

Optional query parameter:

ParameterDescription
currencyCurrency used for report generation

Returns a consolidated team inventory export.

The current controller returns a CSVResponse even for the xlsx suffix.


API — Webhooks

Register a stock webhook

POST /api/stock/webhooks

The current controller accepts request params:

FieldRequiredDescription
urlYesDestination URL
formatNoPayload format. Defaults to form
headersNoCustom headers

Example payload:

{
"url": "https://erp.mycompany.com/hooks/tictap-stock",
"format": "json",
"headers": {
"X-Secret-Token": "my-secret-token",
"X-Source": "TicTAP"
}
}

Response 201: empty body on success.

Webhooks registered this way are linked to the stock store and stock item definitions configured for the team associated with the authentication token. They are automatically subscribed to stock transaction events and stock item threshold events.

Events supported by the stock registration endpoint

The stock webhook registration flow currently supports:

stock_transaction_created
entity_stock_depleted
entity_stock_replenished

stock_transaction_created is emitted for stock movements that affect the team's stores. entity_stock_depleted and entity_stock_replenished are emitted for stock items when their quantity crosses the configured minimum threshold.

Event: stock_transaction_created

The outgoing payload is based on the serialized StockTransaction and is enriched with product_name where it can be resolved.

Example request body sent to the webhook receiver:

{
"id": "01HXYZ...",
"date_time": "2026-04-24T10:30:00+00:00",
"applied": "2026-04-24T10:35:00+00:00",
"comments": "Delivery note 123",
"reason": "Monthly purchase",
"order_number": "ORD-2026-001",
"user_id": "user-uuid",
"movement": {
"type": "input",
"origin": null,
"destination": "Central Warehouse"
},
"lines": [
{
"product_id": "product-uuid",
"product_name": "HDMI Cable 2m",
"quantity": 10.0,
"product_unit_cost": {
"amount": "1299",
"currency": "EUR"
},
"line_cost": {
"amount": "12990",
"currency": "EUR"
}
}
]
}

Notes:

  • product_name is added during webhook enrichment when it can be resolved
  • applied, comments, reason, and order_number may be null
  • the exact serialized shape depends on the current StockTransaction serializer output

Delivery behavior:

  • origin and destination stores are both considered
  • duplicate delivery is prevented when both sides resolve to the same webhook ID
  • configured custom headers are forwarded

Events: entity_stock_depleted and entity_stock_replenished

These events are delivered as entity webhooks for the stock item definition configured in the team's stock settings.

The payload includes:

  • entity: the serialized stock item affected by the change
  • event.name: entity_stock_depleted or entity_stock_replenished
  • event.current_snapshot: the entity snapshot after the change
  • event.data: the serialized event data, including the entity id, definition id, minimum stock, current stock and maximum stock
  • datetime: webhook generation timestamp

For entity_stock_depleted, event.data.replenishment_quantity indicates how many units are needed to reach the effective maximum stock. If maximum stock is not configured or is 0, the minimum stock value is used as the effective maximum for that calculation.


Formats and conventions

Identifiers

  • Most identifiers are UUIDs or ULIDs
  • identifiers are serialized as strings

Dates

  • query filters use Y-m-d H:i:s style values
  • serialized timestamps are typically returned in ISO 8601 form

Quantities

  • transaction quantities are decimal numbers
  • stock item quantities are integer-based in the current domain model
  • minQuantity and maxQuantity are integer-based in stock item write endpoints

Pagination

Paged JSON list endpoints follow the usual structure:

{
"results": [],
"total": 100,
"count": 25,
"pages": 4,
"page": 1,
"limit": 25
}

Errors

Typical error codes used by the current controllers:

CodeMeaning
400Invalid or missing parameters
401Not authenticated
403Access denied
404Entity not found

Many validation failures currently surface as 400.