AWRA OpsHub Search

A Key That Can Only Read

The reporting tool needs to read. The supplier sync needs to write vendors. If both hold the same key, the dashboard holds the power to change your supplier list. Why write access belongs in its own named scope rather than a checkbox on one all-purpose key, and how the developer API draws that line.

Integrations & Data Washingtone Aura 12 min read

The short version

Give every integration its own key, named after the system that holds it, carrying only the scopes it actually uses. A key that only reads should be a key that cannot write by construction, not one whose writing has been switched off. In AWRA a write needs a scope named for writing: no read scope, not the read-everything umbrella, not even a key issued as “everything” before writes existed will ever satisfy it. Reach for a write key only when an integration genuinely creates records, keep it to one module, and give it an expiry.

In March the finance team connects a dashboard tool to the operations system. Somebody issues an API key, pastes it into the dashboard’s connection screen, and the monthly sales and purchasing charts start drawing themselves. Four people in the dashboard tool can see that connection. One of them is a contractor. Nobody thinks about the key again, because nothing about it has ever gone wrong.

In October procurement wants their supplier onboarding form to create vendor records automatically. IT is asked how long that will take, and someone in the meeting says the obvious thing: there is already a key, it already works, just tick the box that lets it write.

Ticking the box takes ten seconds and quietly hands every holder of the dashboard’s key, the contractor included, the ability to change your supplier list. Nobody decided that. It fell out of a convenient shape.

Where a key actually lives

A user account belongs to a person who can be asked questions, trained and offboarded. An API key belongs to a system, and systems are leaky in ways people are not. A key gets copied into an environment file, a no-code tool’s settings, a script on a server nobody owns any more, a support-ticket screenshot, an old agency’s password vault. Every one of those copies holds exactly the power the key holds, for as long as the key is alive.

So the useful question about a key is not “do we trust the integration?” but “what happens on the day a copy of this string is in the wrong hands?” And the answer to that depends almost entirely on whether the key can write.

A leaked key that can only read

  • A disclosure: someone saw records they should not have seen.
  • Bounded by the scopes on the key, so a catalogue key exposes the catalogue and nothing else.
  • Contained by revoking it; nothing in your records needs checking afterwards.

A leaked key that can also write

  • The same disclosure, plus every record changed since the leak is now in question.
  • A supplier’s email address quietly edited is the start of a payment fraud, not just a data problem.
  • Revoking it stops new damage and repairs none of the old.

The asymmetry is the whole argument. Most integrations only ever read: a dashboard, a website stock feed, a nightly extract into a warehouse. They are also the ones most likely to be configured once in a hurry and left alone for years. Giving them write power buys nothing and costs the right-hand column.

Why a checkbox on one key is the wrong shape

There are two ways to give an integration write access. You can make writing a property of a key that already exists, a toggle somebody can flip. Or you can make it a separate, named scope that a key either was issued with or was not. They sound like the same feature. They behave very differently once keys are out in the world.

A toggle widens a credential that has already been distributed. Every copy of the string, everywhere it was pasted, gains the new power at the same instant, and the person flipping the toggle has no way to know where those copies are. A separate scope means a separate key: a new string, issued to one system, named after it, carrying a decision somebody made on the day it was issued. The old reading key stays exactly as weak as it was.

Widening a key you already handed out widens every copy of it at once. Issuing a second key widens nothing.

This is least privilege applied to machines. The difference from people is that a machine never complains about being under-privileged, so the only pressure in the system pushes towards more access, and the shape of the credential has to push back.

How the line is drawn here

The developer API keys are issued in Settings → Webhooks & API by someone holding the permission to manage API keys. Each key carries your organization with it, so no request ever names a workspace; that half of the design is covered in No Route Here Asks Who You Are Asking About. This post is about the other half: what a key is allowed to do once it is in.

Scope What it unlocks
inventory:read Items, categories and warehouses.
sales:read Customers and invoices.
procurement:read Vendors and purchase orders.
developer:read Every read scope above. Writes nothing.
inventory:write Create and update items, and read inventory.
sales:write Create and update customers, and read sales.
procurement:write Create and update vendors, and read procurement.
developer:write Everything, reads and writes.

Four rules sit underneath that table, and they are enforced in the one place every request passes through rather than in each endpoint:

  • Nothing read-flavoured satisfies a write. The read umbrella reaches every read endpoint and none of the write ones. A request to create an item with a reading key is refused with a 403 that says the scope is insufficient and names the scope it would need.
  • A module’s write scope reads that module and nothing else. A key issued for procurement:write can read the vendor it just created; it cannot list your customers. The supplier sync gets suppliers.
  • A key with no scopes is refused, not treated as all-access. An empty list is the most common way a permission system fails open, because “no restrictions recorded” reads the same as “no restrictions”. Here it reads as no access.
  • Scopes are fixed at issue. There is no screen that edits the scopes on a live key. More power means a new key, which is a visible event with a name and a date on it.

The last rule feels like friction, and it is meant to. Narrowing is cheap: issue a narrower key, move the integration across, revoke the old. Widening is the dangerous direction, so it is an act of issuing rather than an edit.

A key does not grow when the product does

The developer API began as reads only. Master-data writes arrived in October 2026, and that created a question most API designs answer by accident: what happens to the keys that already exist?

Some of those keys had been issued with the read umbrella, and a few internal ones with a bare *, which in plain English means “everything”. On the day writes shipped, “everything” could have started to include writing. Every one had been pasted somewhere on the understanding that it could look and not touch, and that would have become false overnight with nobody told.

So it does not. Both * and the read umbrella satisfy read scopes only, and writes need a scope that names writing. A test pins it: the umbrella, the wildcard and a single-module read key are each refused on create, and no item exists afterwards.

The general rule

A permission should mean what it meant on the day somebody approved it. If a release can widen what an existing key does, then nobody approved the wider version; the release did. When you assess any integration platform, ask what happened to old keys the last time it added a capability.

Write is itself narrow

Holding a write scope does not mean a key can do what a person in the product can do. The writes reach master data, the reference records everything else hangs from, and stop there: create and update for items, customers and vendors. There is no delete over the API. A few fields are refused outright with a 422 that names them, even to a key holding the write umbrella:

  • An item’s stock. A new item starts at zero, and stock moves only through adjustments in the product, where counts, reasons and approvals apply.
  • A supplier’s payout details. Bank account and mobile-money payout fields are changed in the product, where a second person can be required and the supplier is told. An integration that could rewrite where a supplier is paid would be the most valuable string in your organization.
  • An item’s type and inventory account after creation. Switching an item between stock and service moves it in or out of every stock path at once, so it is settable on the way in and nowhere else.

The reason is that an API key has no person behind it. Approval controls work by separating the person who asks from the person who agrees, and a key cannot be separated from itself. So anything that moves stock, money or an approval stays where a person does it.

One thing to know, because it is a reason to keep inventory:write rare. In the product, changing an item’s SKU or category is a critical-field change: someone without the approval permission raises a request rather than making the change. An item update through the API applies a new SKU or category directly. Treat a key with inventory:write the way you would treat a person who holds that approval permission, because in effect it does.

Which key each integration should hold

A dashboard or reporting tool

One read scope per area it charts

Sales and procurement charts need sales:read and procurement:read. Resist the read umbrella unless it genuinely reports on all three areas; a narrower key is a smaller disclosure on the bad day.

A website stock or catalogue feed

inventory:read and nothing else

It sits on a public-facing server, which makes it the key most likely to leak. It should expose the catalogue and stop.

A supplier onboarding form

procurement:write on its own key

It creates vendors and needs to read them back. It has no business seeing customers or invoices, and with this scope it cannot.

A CRM pushing new customers

sales:write, named after the CRM

Every customer it creates is recorded in the audit log against that name, so a strange record has an obvious first suspect.

A one-off migration

developer:write with a short expiry

Set it to expire in a week, run the load, then revoke it on the day you finish rather than waiting for the expiry to tidy up after you.

Running the keys once they exist

  1. The token is shown once

    At creation the full key is displayed once and then only a short prefix is kept readable; the stored copy is a hash. If it was not saved, issue a new one rather than looking for the old.

  2. Expiry is optional and worth setting

    Anything from one day to two years. A key with an expiry is a key somebody has to think about again, which is the point.

  3. Last used tells you what is dead

    The key list shows when each key last made a call, stamped at most once a minute. A key reading “Never” a month after issue, or last used in spring, is a key to revoke.

  4. Rotation keeps the scopes

    Rotating revokes the old token immediately and issues a new one with the same scopes and remaining expiry. That makes it the right tool for a suspected leak and the wrong one for narrowing a key; to narrow, issue a fresh key with fewer scopes and revoke the old.

  5. Every write is attributed to the key

    Creates and updates land in the audit log against “API key” plus the key’s name, with the fields an update changed. Name keys after the system holding them, not after the person who issued them.

  6. A key can check itself

    The /me endpoint answers with the organization, the key’s name, prefix, scopes and expiry, for any usable key whatever its scopes. It is the first call an integrator should make, and the quickest way to confirm a key is as narrow as you think.

A create also honours an Idempotency-Key header for twenty-four hours, so a sync that retries after a timeout gets the first result back rather than a second customer; the same header with a different body is refused with a 409. Why that window and that refusal are the right behaviour is argued in The Second Identical Request.

What every key in a workspace shares

Three controls sit above the individual key. The workspace’s IP allowlist binds API keys exactly as it binds people, and is checked before anything else. The plan’s daily call allowance is counted across all of the workspace’s keys together, while the per-minute limit of 120 requests is counted per key. And the number of active keys is a plan limit: on the smallest plans it is one, which makes separating a reader from a writer impossible there. If a second integration is coming, plan the key estate before it arrives.

For when to build on the developer API at all, read The REST API: Tokens, Permissions and Limits. The scope list and error codes are in the API reference, and the setup walkthrough is in the developer API guide.

The straight answer

What AWRA OpsHub does today

  • Read and write as separate named scopes, per module, with a read umbrella that never writes and a write umbrella that has to be chosen on purpose.
  • Keys issued before writes existed stay read-only, the wildcard included, so adding a capability never widened a key already in use.
  • Master-data writes only: create and update for items, customers and vendors, with stock, supplier payout details and an item’s type refused by field.
  • Every write audited against the key’s name, a 24-hour idempotency key on create, expiry from one day to two years, one-time token reveal, rotation and revocation behind their own permission, and a last-used stamp per key.
  • Workspace-wide guards on every key: the IP allowlist, a daily call allowance and a per-key rate limit.

More we can add to your workspace

  • An IP allowlist per key, so the supplier sync answers only from its own server while the dashboard key answers only from the reporting vendor’s range.
  • A daily call allowance per key, so a runaway reporting job spends its own budget and leaves the writer’s untouched.
  • An overlap window on rotation, with the old and new tokens both valid for a set period, so a key can be swapped across several servers without a moment of refused calls.
  • Item-master approval on API updates, routing an SKU or category change from a write key into the same approval a person without that permission raises in the product.
  • A per-key request log of reads, showing which records a key fetched and when, alongside the last-used stamp.
  • Key issuance in the audit log with its scopes, recording what power a key was given at the moment it was given, beside who issued it.

Where we point you to a specialist

  • We do not let an API key approve, post, pay or move stock. Those controls depend on separating the person who asks from the person who agrees, and a key has no person behind it to separate.
  • We do not widen what an existing key can do when the product gains a capability. A new power arrives under a new scope, and reaching it means issuing a key that names it.

The first two lines are one build seen from two sides: once the network limit and the call budget both sit on the key rather than the workspace, each integration’s blast radius is fully its own.

More we can add to your workspace

Anything above that you need, we can build for you

Everything listed above as something we can add describes what ships in the standard product today — it is a starting point, not a limit on what AWRA OpsHub can do for your organisation. Kenya's eTIMS integration and its maintained payroll engine are both in the product because clients needed them and commissioned them; neither appeared by itself, and the same door is open for whatever you just read about. One qualification so this is worth what it claims: a small number of things on this blog we deliberately leave to a specialist rather than build — a statutory ledger we will not sign our name to, a rule that would decide a tax question for you, a clinical or member-funds record that belongs in a regulated system — and where that is true the post says so in those words. Everything else is a scope, a timeline and a price.

The operational work, which is what most commissions actually are

An extra approval stage in a chain that does not match the standard one, a custom field set on employees or assets that only your sector needs, an expiry that has to block an order rather than send an email, a report your board asks for in a shape nothing produces, or a scanner or weighbridge feeding the goods-in door. These are the commissions we are asked for most often and the smallest ones we quote — and unlike a revenue-authority pipeline, none of them waits on a regulator.

The module-shaped additions, which are the ones readers ask for most often

A price list with real discount authority, a customer-facing quotation that expires, a bill of materials or recipe costing, a staff advance that is issued, acquitted and chased, a member or unit ledger, a matching rule that holds a payment. Each of these is a build rather than a setting, and each has been quoted before — a bigger piece of work than a custom field, with a written spec and a date instead of a roadmap slide.

The report, document or pack nothing currently produces

The board pack in the shape your board actually asks for, a donor or funder layout, an invoice or receipt template carrying what your regulator or your customer expects, a dataset the report builder cannot reach yet. Usually the fastest thing on this list to deliver, because the data is already in the system.

Systems, rails and hardware you already run

The accounting package, CRM, online store, core banking or custom database you intend to keep — connected through our API so a fact is entered once and appears everywhere it is needed. Plus the physical edge: a scanner, a scale, a weighbridge or a till peripheral feeding the door it belongs to.

How it works: you describe the requirement, we return a written scope, timeline and cost, and once agreed it is built into your environment and maintained as part of the product. Nothing here waits on a regulator or a published specification, which is why operational builds are the ones we quote fastest. Tell us the requirement that would otherwise rule us out — that is a better first conversation than a demo.

Tell us what your operation needs

Five questions to ask of the integrations you already run

How many systems hold a copy of each key?

What you will usually find

Nobody knows for the oldest one.

How to read it

Then that key has to be treated as already leaked. Issue each system its own, move them across one by one, and revoke the shared one.

Which of our keys can write?

What you will usually find

More than the number of integrations that create records.

How to read it

Every write power that is not used is pure downside. Reissue those integrations on read scopes.

If a supplier record changed overnight, could we tell which integration did it?

What you will usually find

Only if the keys are named after systems.

How to read it

A key named after the person who issued it points at the wrong suspect. Rename by reissuing.

Which keys have not been used in ninety days?

What you will usually find

At least one, often the most powerful.

How to read it

A dormant key carries all the risk of a live one and none of the use. Revoke it, and see who notices.

What happened to our old keys the last time the platform added a capability?

What you will usually find

Nobody checked.

How to read it

Ask the vendor directly. If old keys gained the new power, every key you hold is only as narrow as their next release.

Read the scopes before you issue the key

The API reference lists every scope, every endpoint it unlocks and every refusal code, so you can decide what each integration needs before anyone pastes a string anywhere.

Open the API reference

Frequently asked questions

Can a read-only key ever be used to change data?

No. Writes require a scope named for writing: inventory:write, sales:write, procurement:write or the developer:write umbrella. The read scopes, the read umbrella and keys issued with a wildcard before writes existed are refused on every write endpoint as having insufficient scope.

Can we add write access to a key we already issued?

Not by editing it. Scopes are fixed when a key is issued, so more access means issuing a new key with the write scope and giving it only to the system that needs it. The existing reading key stays exactly as narrow as it was.

What can a write key actually change?

It can create and update items, customers and vendors, the master data other records depend on. It cannot delete records, set an item’s stock, change where a supplier is paid, or change an item’s type after creation. Stock, payments and approvals stay in the product, where a person does them.

How do we see what an integration changed?

Every create and update made through the API is recorded in the audit log against the name of the key that made it, and an update records which fields it changed. Naming each key after the system that holds it makes that log readable.

What does rotating a key do to its scopes?

Rotation revokes the old token immediately and issues a new one with the same scopes and the same remaining expiry. It is the right response to a suspected leak. To reduce what a key can do, issue a new key with fewer scopes and revoke the old one instead.

Does every key in a workspace share the same limits?

The IP allowlist and the daily call allowance apply to the workspace as a whole, so every key is bound by them together. The per-minute rate limit is counted for each key separately.

Share this article

LinkedIn X WhatsApp

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