Appearance
OSERP API integration cookbook
This cookbook turns the endpoint inventory into working integration patterns. The examples use fictional Demo Retail Group LLC data and the /api/v1 prefix. Replace host, IDs, credentials, and route names with the deployed API contract. A route appearing in the inventory does not grant permission or guarantee that a tenant has enabled the module.
1. Build a tenant-safe client
Every request carries the access token and scope headers. Store tokens in a secret manager; never put them in browser local storage or source control.
bash
export OSERP_API="https://api.example.com/api/v1"
export OSERP_TOKEN="replace-with-short-lived-token"
export TENANT="tenant_demo"
export ORG="org_demo"
export COMPANY="company_uae"
curl "$OSERP_API/inventory/items?limit=50" \
-H "Authorization: Bearer $OSERP_TOKEN" \
-H "X-Tenant-Id: $TENANT" \
-H "X-Organization-Id: $ORG" \
-H "X-Company-Id: $COMPANY"The server, not the caller, is authoritative for tenant, company, branch, warehouse, and user ownership. If a caller supplies an ID outside scope, return a clear 403 or 404 according to the security policy; do not leak whether another tenant’s record exists.
2. Read and create an item
Use list calls for synchronisation and detail calls for a single record. Preserve the server ID, code, version, and timestamps.
http
GET /api/v1/inventory/items?companyId=company_uae&updatedAfter=2026-09-01T00:00:00Z&limit=100&cursor=...
Authorization: Bearer <token>
X-Tenant-Id: tenant_demoSample response:
json
{
"data": [{
"id": "item_medjool_001",
"code": "DATE-A-5KG",
"name": "Medjool dates Grade A 5 kg",
"inventoryUom": "KG",
"batchManaged": true,
"active": true,
"updatedAt": "2026-09-16T09:20:00Z"
}],
"nextCursor": "eyJvZmZzZXQiOjEwMH0="
}Create only fields supported by the contract. A client-generated idempotency key prevents retry duplication:
http
POST /api/v1/inventory/items
Idempotency-Key: item-import-20260916-date-a-5kg
Content-Type: application/json
{
"code": "DATE-A-5KG",
"name": "Medjool dates Grade A 5 kg",
"inventoryUom": "KG",
"batchManaged": true,
"companyId": "company_uae",
"categoryId": "cat-produce"
}A successful response should be stored with the key and resource ID. If a timeout occurs, retry the same request with the same key. Never generate a new key until you know whether the first request committed.
3. Update and deactivate safely
Use PATCH for a field change where supported. Include an optimistic version or If-Match value when the API exposes one:
http
PATCH /api/v1/inventory/items/item_medjool_001
If-Match: "v7"
Content-Type: application/json
{"active": false, "deactivationReason": "Season ended"}A 409 indicates that another user changed the record. Read the latest version, show the difference, and let an authorised operator decide. Do not blindly overwrite. Deactivation should preserve historic documents, stock movements, and invoices.
4. Create a supplier and purchase order
First create or locate the supplier. Search by tenant-safe tax number or external ID, not by display name alone:
http
GET /api/v1/business-partners?type=supplier&externalId=novabio-001Then create a purchase order with header scope and lines. Use decimal strings for money when the contract requires exact precision:
http
POST /api/v1/purchasing/orders
Idempotency-Key: po-demo-retail-2026-00041
Content-Type: application/json
{
"companyId": "company_uae",
"supplierId": "supplier_novabio",
"warehouseId": "warehouse_dxb",
"currency": "USD",
"exchangeRate": "3.6725",
"requiredDate": "2026-10-02",
"lines": [{
"itemId": "item_medjool_001",
"quantity": "500",
"uom": "CARTON",
"unitPrice": "42.00",
"taxCode": "ZERO_EXPORT",
"batchExpected": true
}]
}The API should return order status, approval state, totals, document number, and audit timestamps. The integration must not call a “post” action until the required approval state is reached.
5. Receive inventory with batch detail
A receipt references the approved purchase order and records actuals. Do not send ordered quantity when the warehouse received less:
http
POST /api/v1/inventory/receipts
Idempotency-Key: grn-po-demo-retail-00041
{
"purchaseOrderId": "po_00041",
"warehouseId": "warehouse_dxb",
"receiptDate": "2026-10-02",
"lines": [{
"itemId": "item_medjool_001",
"quantity": "480",
"uom": "CARTON",
"batchNumber": "DT261002A",
"expiryDate": "2027-09-30",
"qualityStatus": "QUARANTINE",
"temperature": "5.2"
}]
}The receipt response should include GRN number, stock movement IDs, provisional cost, and discrepancy status. A quality release is a separate controlled action. Keep supplier claim and damage evidence linked to the receipt.
6. Query stock and post a transfer
Use filters for owner, warehouse, item, batch, quality status, and available quantity:
http
GET /api/v1/inventory/stock?warehouseId=warehouse_dxb&itemId=item_medjool_001&status=RELEASEDA branch transfer requires source, destination, item, batch, quantity, reason, and expected date. The source dispatch and target receipt must remain linked:
http
POST /api/v1/inventory/transfers
Idempotency-Key: transfer-dxb-ruh-20261003-001
{"sourceWarehouseId":"warehouse_dxb","targetWarehouseId":"warehouse_ruh",
"lines":[{"itemId":"item_medjool_001","batchNumber":"DT261002A","quantity":"100"}],
"reason":"Branch replenishment","dispatchDate":"2026-10-03"}Do not model a transfer as a sale unless the legal-company relationship and tax policy require an inter-company document. For each movement, capture the resulting status and source document number for reconciliation.
7. Pagination, filtering, and synchronisation
Prefer cursor pagination when available. Store the cursor and last successful timestamp. A robust synchroniser:
- Requests a bounded page with tenant and company filters.
- Validates schema and rejects unknown tenant/company IDs.
- Upserts by immutable server ID or external key.
- Persists the next cursor transactionally with the page.
- Retries transient 429/5xx responses with exponential backoff and jitter.
- Sends permanent 4xx errors to a review queue with request ID.
Use a small overlap on updatedAfter and deduplicate by ID to avoid missing records created at a timestamp boundary. Never use an unbounded “return everything” call in a scheduled job.
8. Webhooks and exactly-once effects
Subscribe only to events required by the integration: purchase.approved, inventory.receipt.posted, sales.invoice.posted, and payment.settled. Verify signature, timestamp tolerance, event ID, and tenant before processing. Store the event ID before applying an irreversible side effect.
json
{
"id": "evt_01J8...",
"type": "inventory.receipt.posted",
"tenantId": "tenant_demo",
"occurredAt": "2026-10-02T11:42:00Z",
"data": {"receiptId":"grn_0041","warehouseId":"warehouse_dxb"}
}Return 2xx only after durable acceptance. If downstream processing is delayed, place the event in a queue and expose status. A duplicate webhook must result in no duplicate journal, stock, invoice, or notification.
9. Error handling and support evidence
Capture HTTP status, OSERP error code, request ID, correlation ID, endpoint, tenant, company, idempotency key, payload hash, and retry count. Redact tokens and personal data. Typical handling:
| Response | Meaning | Action |
|---|---|---|
| 400 | Invalid field or schema | Fix mapping; do not retry unchanged |
| 401 | Token expired/invalid | Refresh or re-authenticate |
| 403 | Scope or permission denied | Request correct role; no bypass |
| 404 | Not found or hidden by isolation | Verify scope and ID |
| 409 | Duplicate or version conflict | Read current state and reconcile |
| 422 | Business rule failure | Show field-level reason to operator |
| 429 | Rate limit | Back off using server guidance |
| 5xx | Temporary server failure | Retry bounded times, then queue |
Support tickets should include correlation ID and a reproducible redacted request, never a password or bearer token.
10. Integration acceptance checklist
Before production, test tenant isolation, company and warehouse scope, permission denial, duplicate POST, timeout retry, webhook replay, pagination boundary, batch and serial traceability, currency precision, tax validation, period lock, approval gates, and audit log. Compare API-created documents with the same documents created in the UI. Confirm every posted document has a source, actor, timestamp, journal or stock impact, and drill-down.
Practice: import 50 items, create a USD purchase order, retry it after a simulated timeout, receive two batches with one shortage, transfer released stock, replay the receipt webhook, and reconcile API results to the OSERP UI. Document request IDs, statuses, idempotency behavior, and final ledger impact.
Related: API Library, All endpoint inventory, Sales lifecycle, Inventory, and Integration safety.
Generic CRUD contract for AI and connector authors
When an endpoint supports CRUD, document all four operations with the same resource identity and scope rules. GET /resources lists only records visible to the supplied tenant/company scope; GET /resources/:id returns one record or a policy-safe 404; POST creates exactly one record under an idempotency key; PATCH changes permitted fields using a version or If-Match; DELETE should normally deactivate or archive business records rather than erase history. Return id, stable code, status, version, createdAt, updatedAt, scope identifiers, and correlation ID.
Example lifecycle for a business partner:
http
GET /api/v1/business-partners?type=customer&limit=50&cursor=...
POST /api/v1/business-partners
PATCH /api/v1/business-partners/bp_004281
POST /api/v1/business-partners/bp_004281/actions/deactivateThe connector stores the original request hash, response, idempotency key, and last version. A 409 is a state conflict, not a signal to overwrite. Read the latest resource, present the changed fields, and require an authorised decision.
Endpoint documentation template
Every new endpoint entry should state purpose, method/path, required scope headers, permission, request schema, response schema, decimal precision, pagination, idempotency, optimistic concurrency, error codes, audit event, webhook event, and example. Distinguish a route that exists in source from one enabled for a tenant. Mark fields as required, optional, nullable, server-generated, immutable, or policy-dependent. This makes the endpoint understandable to both a human consultant and an AI tool without guessing.
CRUD acceptance tests
Create a record, read it by ID, list it with a cursor, update one permitted field, attempt an immutable-field update, replay the create with the same key, replay with a changed payload, test another company scope, test missing permission, deactivate it, and confirm historical documents still resolve. Record status, error code, request ID, version, audit event, and whether any journal, stock, tax, or notification side effect occurred.