Skip to main content

Inventory adjustment

Business scenario

When a merchant performs a physical stock count — or when a stock count event is triggered in the integrated system — the actual quantities on the shelf must be recorded in Qoyod. Without this step, Qoyod's inventory records reflect a calculated quantity derived from sales and purchase documents, which may drift from physical reality over time. An inventory adjustment corrects that drift by recording what was actually counted.

This use case covers the integrated system to Qoyod direction: your integration detects that a stock count has occurred (whether in real time or as a scheduled batch), then sends a request to Qoyod to create an inventory adjustment. Qoyod computes the difference between the submitted actual quantity and the quantity it currently holds on record, then posts the corresponding accounting entries to the revenue account (for positive adjustments — stock gained) and the expense account (for negative adjustments — stock lost).

If this step is skipped or fails silently, Qoyod's stock records do not reflect physical reality. Downstream documents — invoices, purchase orders — may reference incorrect availability data, and the general ledger will not carry the correcting entries that an audit would expect.

When to use this

Use this when:

  • A physical stock count has been completed in the integrated system and the actual quantities must be recorded in Qoyod.
  • A stock synchronization event requires overwriting Qoyod's current calculated quantity with the physically verified figure.
  • You need to post the accounting entries that accompany a stock gain or loss (revenue and expense account postings).

Do not use this when:

  • You need to move stock between two Qoyod inventory locations — that is an inventory transfer, which is a separate use case.
  • You need to create the products that will be adjusted — products must exist in Qoyod before an adjustment can reference them. See New product creation (UC-01).
  • You need to create or manage the inventory location itself — locations must exist before being referenced in an adjustment.

Prerequisites

  • A valid OAuth 2.0 access token, or an API key, for the Qoyod account you are integrating with. Include it as a request header on every request.
  • The Qoyod ID of the inventory location where the stock count took place. Retrieve this by querying GET /2.0/inventories if you do not already hold it.
  • The Qoyod product ID for each product being adjusted. Obtained at product creation (UC-01) and stored in your integration's data store.
  • The Qoyod account ID of the revenue account to use for positive stock adjustments (stock increases). This account must exist in Qoyod before the adjustment is submitted.
  • The Qoyod account ID of the expense account to use for negative stock adjustments (stock decreases). This account must exist in Qoyod before the adjustment is submitted.
  • At least one product to adjust — the line_items array must contain a minimum of one item.

Sequence diagram

Step-by-step

1. Gather the required IDs

Before building the request, confirm you have four Qoyod IDs stored: inventory_id (the location), revenue_account_id, expense_account_id, and the product_id for each item in the stock count. All four are integers. If any is missing, retrieve it from Qoyod before proceeding — the endpoint will return a validation error (inside a 200 OK response) if required IDs are absent or invalid.

2. Build the request body

Wrap the adjustment object under the inventory_adjustment key. Include at least one item in line_items. Send actual_quantity as a string, not a number — the spec defines it as a string type, and sending a numeric value may cause unexpected behavior.

Request body
{
"inventory_adjustment": {
"inventory_id": 12,
"revenue_account_id": 301,
"expense_account_id": 502,
"date": "2026-05-18",
"description": "Post-count correction — warehouse A",
"line_items": [
{
"product_id": 88,
"actual_quantity": "50",
"rate": "12.50"
},
{
"product_id": 91,
"actual_quantity": "120",
"rate": "4.00"
}
]
}
}

date defaults to today if omitted. description is optional but recommended for audit clarity. rate (cost per unit) is also optional and must be a string if provided.

3. Send the request

Send the request
curl -X POST "https://api.qoyod.com/2.0/inventory_adjustments" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {access_token}" \
-d @request-body.json

Replace {access_token} with your OAuth 2.0 access token. If you are using an API key instead, replace the Authorization header with the header format your Qoyod account uses for API key authentication.

4. Inspect the response body — do not rely on the status code alone

warning

This endpoint returns 200 OK for both success and validation failure. You cannot determine the outcome from the HTTP status code. You must read the response body and check which top-level key is present.

If the response body contains an inventory_adjustment key, the adjustment was created successfully. Capture the id field from the returned object and store it in your integration's data store.

Response — 200 OK (success)
{
"inventory_adjustment": {
"id": 4471,
"date": "2026-05-18",
"description": "Post-count correction — warehouse A",
"created_at": "2026-05-18T12:00:00.000Z",
"updated_at": "2026-05-18T12:00:00.000Z",
"line_items": [
{
"id": 9201,
"product": { "id": 88, "name": "Widget A" },
"quantity": 50,
"value": 12.5
}
],
"revenue_account": { "id": 301, "code": "4001", "name_en": "Sales Revenue" },
"expense_account": { "id": 502, "code": "5001", "name_en": "Cost of Goods Sold" }
}
}

If the response body contains an errors key, validation failed. Read the error messages, correct the request, and retry.

Response — 200 OK (validation failure)
{
"errors": {
"inventory": ["must exist"],
"line_items": ["is invalid"]
}
}

5. Store the adjustment ID

Once you confirm the inventory_adjustment key is present in the response, store the returned id in your integration's data store. There is no GET endpoint for inventory adjustments, so the id returned at creation is the only record reference you will have.

Field mapping

The table below maps concepts from your integrated system to the fields Qoyod expects in the inventory_adjustment request body.

Integrated system conceptQoyod fieldRequiredNotes
Inventory locationinventory_idYesInteger. The Qoyod ID of the warehouse or logical location being adjusted.
Revenue accountrevenue_account_idYesInteger. Account used for posting positive stock adjustments (gains). Must exist in Qoyod.
Expense accountexpense_account_idYesInteger. Account used for posting negative stock adjustments (losses). Must exist in Qoyod.
Adjustment datedateNoString in YYYY-MM-DD format. Defaults to today if omitted.
Description / notesdescriptionNoFree-text string. Useful for audit trail; not required.
Product (line item)line_items[].product_idYes (per item)Integer. The Qoyod product ID.
Actual counted quantityline_items[].actual_quantityYes (per item)String, not a number. The physical count to record.
Cost per unitline_items[].rateNo (per item)String, not a number. Cost per unit for accounting entries.

Two fields in the line item spec warrant extra attention. actual_quantity and rate are both typed as strings in the OpenAPI spec — pass them as quoted values ("50", not 50). Sending them as JSON numbers may produce unexpected results.

current_qty is an optional line-item field that represents the system quantity at the time of the count.

Verification

A 200 OK status code does not confirm success — see Step-by-step above. Success is confirmed when the response body contains the inventory_adjustment key and the returned object has a non-null integer id.

To verify the adjustment was recorded, check the returned object fields:

  • id is present and is a positive integer.
  • date reflects the date you submitted (or today's date if you omitted it).
  • line_items contains one entry per product you submitted, each with a quantity value matching what you sent as actual_quantity.
  • revenue_account and expense_account objects are present, with id values matching the account IDs you submitted.

Because there is no GET /2.0/inventory_adjustments/{id} endpoint, you cannot retrieve the adjustment after the fact via the API. Store the full response body if your integration requires a local audit record. To confirm that stock levels changed in Qoyod, retrieve the affected product records and check the inventories array in each product response for the updated stock value at the relevant location.

Error scenarios

ConditionHTTP statusResponse bodyWhat to do
Validation failure (missing required field, invalid ID, empty line_items)200 OKBody contains errors key with field-keyed arrays of message stringsRead the errors object, identify the failing fields, correct the request body, and retry.
Required field absent (inventory_id, revenue_account_id, expense_account_id, or line_items)200 OKBody contains errors keyEnsure all four required fields are present in the inventory_adjustment wrapper.
Invalid inventory_id (location does not exist)200 OKBody contains errors key, likely inventory: ["must exist"]Confirm the inventory location ID by querying GET /2.0/inventories.
Invalid product_id in a line item200 OKBody contains errors key, line_items entryConfirm each product ID against Qoyod. Products must exist before they can be adjusted.

The key pattern: any failure this endpoint surfaces comes back as 200 OK with an errors body, not as a 4xx status code. Your integration's error-handling logic must branch on the response body, not on the HTTP status.

For retry logic and backoff strategy, see error handling.