IwiConnect API docs

Start here

API keys

Every request carries a key. The key identifies a team, an environment and a set of scopes, and it can do nothing the team behind it cannot already do in the portal.

Creating a key

Only a team owner can create keys. If your role is Member, ask an owner, or ask them to promote you first (see teams).

  1. Open the profile menu, then API keys. You will see every key on the team, who created it, when it was last used, and its scopes. Secrets are never shown again after creation.
  2. Check which account you are in. The environment follows the account rather than being a choice at creation. A key made on a live account is a live key, a key made on a sandbox account is a sandbox key, and a sandbox key never emails an iwi or spends a submission token. Switch accounts first if you are in the wrong one.
  3. Pick scopes. Start with read access and add write scopes as you need them. The full list is on the scopes page.
  4. Name it after the system that will use it. The name is what the portal lists it under, and what you match against when you come to revoke it.
  5. Copy the secret. It appears once, at creation. Lose it and you create a new key rather than recovering the old one.

Key format

Keys are prefixed so you can tell at a glance what you are holding, and so secret scanners can spot one in a commit.

PrefixEnvironmentEffect
iwk_live_ProductionReal projects, real notifications, real spend.
iwk_test_SandboxIsolated data. Nothing leaves the platform, nothing is billed.

The portion after the prefix is random and carries no meaning. Do not parse it. Only the hash is stored, so nobody at IwiConnect can read your secret back to you, including support.

Sending a key

Put it in the Authorization header as a bearer token. Query string authentication is not supported, because URLs end up in logs, proxies and browser history.

Request
curl https://api.iwiconnect.com/projects \
  -H 'Authorization: Bearer iwk_live_7Qx4...' \
  -H 'Content-Type: application/json'
Node
const response = await fetch("https://api.iwiconnect.com/projects", {
  headers: {
    Authorization: `Bearer ${process.env.IWICONNECT_KEY}`,
    Accept: "application/json"
  }
});

const { success, response_object } = await response.json();

What a key can reach

A key inherits the team it was created under and cannot cross that boundary. If you belong to three councils, you need three keys. There is no account switching header, and no way to widen a key's reach after the fact beyond changing its scopes.

Two limits sit on top of that:

Rotating a key

Rotation is a create, swap, revoke sequence. There is no atomic rotate endpoint, deliberately, so you always control the overlap window.

  1. Create the replacement with the same scopes and a name that says what it replaces.
  2. Deploy it to the system that uses it, and confirm traffic is flowing on the new key using the last-used timestamp in the portal.
  3. Revoke the old one. Revocation takes effect immediately. In-flight requests holding the old key start returning 401.

Rotate on a schedule you can live with, and immediately if a key has been in a screenshot, a support ticket, a repo or a CI log.

Storing keys

Treat a key like a database password. Keep it in your secret manager, not in source control, not in a frontend bundle, and not in a Postman collection you share with the team. A key in browser JavaScript is a key published to the internet, and the projects:send scope spends money.

If a key leaks, revoke it first and investigate second. Revocation is instant and free. Email support@iwiconnect.com if you need help working out what was done with it, and include the key's name and the last four characters, never the secret itself.

Checking what a key is

GET /user tells you which team the key resolves to, what role it inherits and how many submission tokens are left. It needs no scopes. Calling it at startup is a cheap way to fail loudly on a misconfigured deployment instead of discovering it on the first write.

Response
{
  "success": true,
  "message": "User retrieved",
  "status_code": 200,
  "response_object": {
    "id": "0b6a51d2-7a2e-4c8e-9f0a-2c2b6b9a1d44",
    "first_name": "Aroha",
    "last_name": "Rangi",
    "teams": [
      {
        "id": "3f1b0c77-9a5d-4d0b-8c3e-1f2a4b6c8d90",
        "name": "Kāpiti Coast District Council",
        "current_credit_balance": 12,
        "role": "OWNER",
        "account": { "national": false, "sandbox": false }
      }
    ]
  }
}