AWRA OpsHub Search
/api/v1 · Developer API

Your workspace’s data, over HTTPS, with a key that already knows whose it is.

Read items and stock, customers and invoices, vendors and purchase orders. Create and update items, customers and vendors. Every call carries one API key, the key carries your organization, and each key holds only the scopes you gave it — so a reporting script can read and never write.

19 endpoints13 that read across inventory, sales and procurement, and 6 that create or update master data
The key is the organizationno workspace ID in any request, and another organization’s record answers 404
Writes by name onlya write needs a write scope; developer:read and * never satisfy one
120 a minute, per keyplus a daily allowance for the whole workspace, set by your plan
Settings → API keys

One key per integration, and the key says who it belongs to.

Someone who holds the permission to manage API keys mints one, names it after the system that will use it, ticks its scopes and, if you want it to lapse, gives it an expiry. The token is shown once. Send it as a bearer token, or in an X-Api-Key header when your proxy keeps Authorization for itself.

New key · CRM sync
awra_9fK2qLxT…48 random characters…
Scopes
sales:readsales:write
Expires
in 365 days (optional, up to 730)
Stored as
a SHA-256 hash and the first ten characters, so the list can show which key is which
Shown
once, on the screen that minted it
GET /api/v1/me
# the organization, the key’s name, prefix,
# scopes, created and expiry dates
  • Your organization is carried by the key

    No route accepts an organization or workspace identifier from the caller. Every query is filtered on the key’s workspace, and a record that exists in another organization answers 404, so the API never confirms that an ID exists elsewhere.

  • Rotate or revoke

    Rotating revokes the old token at once and issues a new one with the same scopes and expiry. Revoking stops a key immediately. A revoked, inactive or expired key answers 401.

  • Your IP allowlist binds keys too

    If the workspace limits sign-in to certain addresses under Settings → Security, API calls from anywhere else are refused with 403 ip_not_allowed, before they count against any limit.

  • How many keys

    Your plan sets how many active keys the workspace can hold. Separate keys per integration let you revoke one system without touching the others.

Scopes

Reading is one decision. Writing is a second one, made by name.

Writes arrived on 15 October 2026. A key minted before that — developer:read or * — went on reading everything and did not gain the power to change a record. To write, a key needs the module’s write scope or the developer:write umbrella.

What each Developer API scope allows
Scope on the key Read inventory Read sales Read procurement Write items Write customers Write vendors
inventory:read—————
sales:read—————
procurement:read—————
developer:read or *———
inventory:write————
sales:write————
procurement:write————
developer:write

A module’s write scope also reads that module. A key with no scopes at all is refused rather than treated as all-access. /me answers any valid key, however narrow, so you can check a key before wiring it in.

The whole surface

Nineteen endpoints, grouped by the scope that opens them.

The API reference lists every filter and a sample response for each, and the same catalog is published as an OpenAPI document at /openapi.json for client generators.

Identity

any valid key
GET/me

Inventory

inventory:read · inventory:write
GET/items
GET/items/{id}
GET/categories
GET/warehouses
POST/items
PATCH/items/{id}

Sales

sales:read · sales:write
GET/customers
GET/customers/{id}
GET/invoices
GET/invoices/{id}
POST/customers
PATCH/customers/{id}

Procurement

procurement:read · procurement:write
GET/vendors
GET/vendors/{id}
GET/purchase-orders
GET/purchase-orders/{id}
POST/vendors
PATCH/vendors/{id}
Writes

Master data goes in over the API. Stock, money and approvals stay where their controls run.

An API key has no person behind it to keep out of their own approval, so the writes are the records an integration most often needs to keep in step — the item list, the customer list, the supplier list — and nothing that moves a balance.

A retry is not a duplicate

Send an Idempotency-Key header on a POST and a repeat of the same request returns the first result instead of creating a second item, customer or vendor.

Every write is on the audit log

Each create and update is recorded with the API key’s name as the actor, so the audit log shows which integration changed a record, not just that one did.

The answer is the record

A create returns 201 with the record in the same shape the read endpoints use, and an update returns it as it now stands. Send only the fields you are changing.

Your item rules still apply

Item creates and updates go through the same item master checks as the screens. A SKU must be unique in your organization, and a vendor’s email must be too.

Stock is refused

A new item starts at zero. A stock field is refused with 422; stock moves through adjustments, receipts and sales in the product. An item’s type is set when it is created and cannot be patched later.

Payout details are refused

Bank and mobile-money fields on a vendor answer 422. Where a supplier is paid changes in the product, where a second person can be required and the supplier is told. Reads leave those fields out as well.

Reading at scale

Built for a nightly sync, not only for one lookup.

  1. Filter on the server. Items by search, category, type or low stock; invoices by status, customer, issue dates or unpaid; purchase orders by status, payment status, vendor or order dates; vendors by active or preferred.
  2. Ask for what changed. updated_since returns records changed at or after a timestamp. An unparseable value is refused with 422 rather than ignored — an ignored filter would hand you a full export while you believed you had a delta.
  3. Walk a whole resource with a cursor. paginate=cursor makes page 1,000 cost what page 1 costs, and no row is skipped or repeated while records change underneath you. Follow links.next until it is null.
  4. Or page by number. 25 a page by default and up to 100, with a total, for a screen that needs “page 3 of 12”.
  5. React instead of polling. A customer or vendor created over the API raises the same customer.created and vendor.created webhooks as one created on screen, so your other systems hear about it once.
Incremental stock sync
GET /api/v1/items?updated_since=2026-10-27T00:00:00Z
      &paginate=cursor&per_page=100
Authorization: Bearer $AWRA_API_KEY
Response · cursor mode
{
  "data": [ … ],
  "meta":  { "pagination": "cursor", "per_page": 100, "has_more": true },
  "links": { "next": "…?cursor=eyJpZCI6MTAwfQ", "prev": null }
}
# stop when links.next is null; never build a cursor yourself
Limits and answers

Two limits, and an error that tells you which one.

120requests a minute, per key

Counted against the key, not the IP address, so two customers behind one office network do not share a budget. Over it, a 429 with Retry-After.

Dailycalls a day, per workspace

Shared by all of the workspace’s keys and set by your plan; the plan comparison lists each figure. It resets at midnight UTC.

0spent by a bad key

The daily count is taken only once a key is known to be valid, in scope and from an allowed address, so a stray token never uses up your allowance.

401unauthorizedNo key, or a key that is unknown, revoked or expired. Check the credential; retrying will not help.
403insufficient_scopeA valid key without the scope this endpoint needs. The message names the scope required.
403ip_not_allowedThe workspace has an IP allowlist and the call came from outside it.
404not_foundNo such record in your organization, including records that exist in someone else’s.
422invalid_parameterA malformed query parameter. A write that fails validation, including a refused field such as stock, also answers 422 and names the field.
429daily_quota_exceededThe workspace has used today’s calls. Backing off within the day will not help.

Developer API — what ships today, and what we can add

What AWRA OpsHub does today

  • Thirteen read endpoints for your identity, items, categories, warehouses, customers, invoices with their lines, vendors and purchase orders with their lines.
  • Six write endpoints that create and update items, customers and vendors.
  • Keys minted in Settings, shown once, stored as a hash, with an optional expiry of up to two years, and rotated or revoked from the same screen.
  • The organization carried by the key, with no identifier accepted from the caller and a 404 for any record outside it.
  • Read scopes per module and write scopes by name, so a read key or an all-access key minted before writes never writes.
  • Idempotent creates with an Idempotency-Key header, and every write on the audit log against the key.
  • Server-side filters, updated_since, numbered pages and cursor paging on every list.
  • 120 requests a minute per key, a daily allowance per workspace set by the plan, and the workspace’s IP allowlist applied to keys.
  • A public reference and an OpenAPI document generated from the same catalog as the routes.

More we can add to your workspace

  • Invoice, order and payment writes, created as drafts that still go through your approval rules before they count.
  • Stock adjustments over the API, raised as pending adjustments for a person to approve, the way count variances are.
  • Read endpoints for more modules, such as accounts and journals, fixed assets, employees and helpdesk tickets.
  • Delete and archive endpoints, routed to the trash so a removal can be restored.
  • Webhook subscriptions managed with an API key, alongside the settings screen that manages them today.
  • Official client libraries for PHP, JavaScript and Python, generated from the OpenAPI document.
  • Batch endpoints that take many records in one call with a per-record result.
  • Sign in with AWRA for third-party apps, so a vendor’s app asks your people for access rather than being handed a key.

Where we point you to a specialist

  • We will not accept a workspace identifier from the caller. The key decides whose data it is; a request cannot ask for someone else’s.
  • We will not let a read key start writing. A key minted to read stays a read key, whatever scopes are added to the API later.
  • We will not take a supplier’s bank or mobile-money details over the API. Where money is sent changes in the product, under dual control, with the supplier told.

Draft invoice writes and read endpoints for accounting are the two items in that middle column integrators ask for first. Tell us what your integration needs to send or read and we will come back with a written spec, a timeline and a price.

Frequently asked questions

How do I get an API key?
Someone with the permission to manage API keys mints one under Settings → API keys, chooses its scopes and, optionally, an expiry of up to two years. The token starts with awra_ and is shown once. Send it as a bearer token, or in an X-Api-Key header.
Do I pass a workspace or company ID with each request?
No. The key carries your organization, and no route accepts an organization identifier from the caller. A record that belongs to another organization answers 404, so the API never confirms that it exists.
Can the API create and update records?
Yes, for master data. Since 15 October 2026 it creates and updates items, customers and vendors with POST and PATCH. Stock, invoices, orders, payments and approvals stay in the product, where their controls run.
Will my existing read key start writing?
No. A write needs a write scope by name — inventory:write, sales:write, procurement:write — or the developer:write umbrella. Keys holding developer:read or * read everything and never write.
What stops a retry creating a duplicate?
Send an Idempotency-Key header on a POST. A repeat of the same request returns the first result instead of creating a second record.
What are the rate limits?
120 requests a minute per key, plus a daily number of calls for the whole workspace set by its plan and shared across its keys, resetting at midnight UTC. A call with an invalid key does not count against the daily figure.
Can I change a supplier’s bank details over the API?
No. Bank and mobile-money fields are refused with a 422 and are left out of every vendor response. They change in the product, where a second person can be required and the supplier is told.
Is there an OpenAPI description?
Yes. The reference at /api-docs and the OpenAPI document at /openapi.json are both generated from the same catalog as the routes.

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center