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
- Overview
- Authentication
- Domain concepts
- API — Product catalogue
- API — Stores and inventory
- API — Stock transactions
- API — Reports and export
- API — Webhooks
- 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:
| Mode | Description |
|---|---|
catalog | Products are managed in the dedicated stock catalogue. Product CRUD and bulk import are supported. |
entities | Products 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/storesStoreNode: nodes in the store hierarchy, used in summaries, children, replenish views, and transactions
Movement types
| Type | Description |
|---|---|
input | Goods received |
output | Goods consumed or dispatched |
transfer | Move between nodes |
adjust | Manual 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:
| Parameter | Type | Description |
|---|---|---|
name | string | Filter by name |
categoryId | string | Filter by category |
query | string | Search by name or part number |
ownerId | string | Owner enterprise ID |
ownerIdmust 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"
}
| Field | Required | Description |
|---|---|---|
name | Yes | Product name |
unitCostAmount | Yes | Unit cost amount |
unitCostCurrency | No | Currency code. Defaults to EUR in the current controller if omitted |
description | No | Description |
partNumber | No | SKU / reference |
qrCodes | No | Product QR codes |
categoryId | No | Category ID |
manufacturerId | No | Manufacturer ID |
image | No | Image URL |
id | No | UUID 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"
}
]
}
| Field | Required | Description |
|---|---|---|
partNumber | Yes | Upsert key |
name | Yes | Product name |
unitCost | Yes | Unit cost amount |
description | No | Description |
manufacturerName | No | Manufacturer name. Created automatically if missing |
categoryName | No | Category name. Created automatically if missing |
image | No | Image URL |
id | No | Explicit 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:
| Parameter | Type | Description |
|---|---|---|
scope | string | team 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:
jsoncsvxlsx
Returns stockable reads in the subtree that need replenishment.
The current controller returns a CSV response path for non-JSON exports, even for the
xlsxsuffix.
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:
productIdquantityminQuantitymaxQuantity
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:
quantityminQuantitymaxQuantity
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:
jsoncsvxlsx
Query parameters:
| Parameter | Type | Description |
|---|---|---|
from_date | datetime Y-m-d H:i:s | Start date |
to_date | datetime Y-m-d H:i:s | End date |
product_id | string | Product ID |
origin_store | string | Origin store ID |
destination_store | string | Destination store ID |
origin_node | string | Origin node ID |
destination_node | string | Destination node ID |
quantity | number | Quantity filter |
order_number | string | Partial match |
reason | string | Partial match |
user_id | string | Creator user ID |
movement_type | string | input, output, transfer, adjust |
page | integer | Page number |
limit | integer | Page size |
At least one of the following is required:
origin_storedestination_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
xlsxsuffix.
Transaction summary
GET /api/stock/transactions/summary?store=<store_id>&from=<datetime>&to=<datetime>
Required query parameters:
storefromto
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[]fromto
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:
csvxlsx
Optional query parameter:
| Parameter | Description |
|---|---|
currency | Currency used for report generation |
Returns a consolidated store inventory export.
The current controller returns a
CSVResponseeven for thexlsxsuffix.
Team inventory report
GET /api/stock/team/report.{format}
Supported route formats:
csvxlsx
Optional query parameter:
| Parameter | Description |
|---|---|
currency | Currency used for report generation |
Returns a consolidated team inventory export.
The current controller returns a
CSVResponseeven for thexlsxsuffix.
API — Webhooks
Register a stock webhook
POST /api/stock/webhooks
The current controller accepts request params:
| Field | Required | Description |
|---|---|---|
url | Yes | Destination URL |
format | No | Payload format. Defaults to form |
headers | No | Custom 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_nameis added during webhook enrichment when it can be resolvedapplied,comments,reason, andorder_numbermay benull- the exact serialized shape depends on the current
StockTransactionserializer 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 changeevent.name:entity_stock_depletedorentity_stock_replenishedevent.current_snapshot: the entity snapshot after the changeevent.data: the serialized event data, including the entity id, definition id, minimum stock, current stock and maximum stockdatetime: 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:sstyle 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
minQuantityandmaxQuantityare 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:
| Code | Meaning |
|---|---|
400 | Invalid or missing parameters |
401 | Not authenticated |
403 | Access denied |
404 | Entity not found |
Many validation failures currently surface as 400.