Integrator onboarding¶
This page answers one question the rest of the guides assume you already know: which parts of an Invora integration can you set up yourself, and which ones need someone at Invora to enable?
Every other guide describes how a capability works. This one describes how you get it — and says plainly where the route does not exist yet. If a row below says No, that is a statement about today, not a promise being deferred: an aspirational entry here would recreate the exact problem this page exists to fix.
How to read the table
Yes — you can do it in the dashboard or over the API, with no Invora involvement. Partial — a route exists but it is restricted, environment limited, or only half-working; the How column says which. No — there is no self-service route today; you have to ask us.
What is self-service today¶
| Capability | Self-service today? | How |
|---|---|---|
| Register and sign in as a human user | Yes | Sign up at dashboard.invora.app. Interactive apps use authorization_code + PKCE — see Authentication. |
| Create your organization and start a trial | Yes | Complete the business profile in the dashboard, or call POST /api/identity/v2/registration/complete-profile with the signed-in user's token — see Getting Started. |
| Create a machine user (service account) | Partial | Dashboard → Settings → Developer. Only an organization owner can open that page today, so a non-owner integrator cannot create one — tracked at invora-flutter#260. |
Obtain a client_secret for client_credentials |
Partial | Dashboard → Settings → Developer → Generate client secret, on the machine user. The secret is shown once and cannot be read back. Shipped for staging; not yet released to production — tracked at invora-flutter#257. |
Grant that machine user its Invora.* permissions |
Partial | Dashboard → Settings → Developer → Request API access, on the same machine user. You can only grant permissions your organization has already been delegated; if it has none, the dialog says so and enabling them is an Invora-side step — tracked at invora-flutter#257. |
| Mint a personal access token (PAT) or JWT key | Partial | Dashboard → Settings → Developer. Same owner-only restriction as creating the machine user, and PATs are for development only — tracked at invora-flutter#260. |
| Enable the ZATCA regulation for your tenant | No | Ask Invora. Enabling a regulation and setting its base configuration for a tenant is an administrator operation — see ZATCA integration. Tracked at invora/business#568. |
| Run ZATCA onboarding (CSR → compliance CSID → production CSID) | Yes | POST /api/v1/regulations/zatca/onboarding, once the regulation above is enabled and your tax identity is complete — see ZATCA integration. |
| Receive signed webhooks | Partial | Endpoints can be registered, but outbound signing is currently broken on both algorithms, so deliveries dead-letter on the first attempt. Do not build against webhook delivery yet — tracked at invora-backend#228. |
| Connect Tap as a payment provider | Yes | POST /api/billing/v2/integrations/tap with your own Tap API key — see Collect payments with Tap. |
A credential with no permissions is not a working credential
A freshly created machine user holds no Invora.* permissions. Its
client_credentials call will succeed and return a token, and then every
API call made with that token will fail with PERMISSION_DENIED (HTTP 403).
Generating the secret and granting the permissions are two separate steps —
do both.
Environment matrix¶
These are the values every other guide refers to. Every cell below is rendered
from extra.environments in guides/mkdocs.yml — the same single source the
examples and the CI environment gates read — so if a page anywhere else
disagrees with this table, this table is right.
| Production | Staging | |
|---|---|---|
| API gateway | https://gateway.invora.app |
https://stg-gateway.invora.app |
| Auth issuer | https://auth.invora.app |
https://stg-auth.invora.app |
| Token endpoint | https://auth.invora.app/oauth/v2/token |
https://stg-auth.invora.app/oauth/v2/token |
| Discovery document | auth.invora.app/.well-known/openid-configuration |
stg-auth.invora.app/.well-known/openid-configuration |
| Dashboard | https://dashboard.invora.app |
https://stg-dashboard.invora.app |
| Project-audience id | 372376660185448530 |
372376692817133647 |
gRPC uses the same gateway host on port 443 over TLS — see gRPC & Transcoding.
The two reserved scopes¶
Machine-to-machine tokens must request both of these alongside openid.
Neither is optional, and each fails differently when it is missing:
| Scope | What it does | Symptom when missing |
|---|---|---|
urn:zitadel:iam:org:project:id:<PROJECT_ID>:aud |
Puts the Invora project in the token's audience, which is what makes the gateway accept the token at all. <PROJECT_ID> is the project-audience id of the environment you are calling — see the table above. |
The gateway rejects the token. |
urn:zitadel:iam:user:resourceowner |
Embeds the caller's home organization in the token, which is how a request with no explicit x-zitadel-orgid header resolves to your own tenant. |
The token is valid but the API cannot resolve a tenant, so calls return PERMISSION_DENIED (HTTP 403). |
Putting it together, against production:
curl -s -X POST https://auth.invora.app/oauth/v2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
--data-urlencode "scope=openid urn:zitadel:iam:org:project:id:372376660185448530:aud urn:zitadel:iam:user:resourceowner"
Moving an example between environments changes three values
The gateway host, the auth issuer, and the project-audience id. The
third is the one that gets missed: an id identifies the Invora project on a
specific issuer, so presenting the staging id to the production token
endpoint (or the reverse) fails with invalid_client and no token is
issued. Swapping the host alone is not enough.
How machine-to-machine access is authorized¶
Getting an M2M credential to actually work is two independent things, and confusing them is the single most common way an integration stalls:
- Authentication — your machine user has a
client_secret, and the token request carries both reserved scopes above. This gets you a token. - Authorization — that machine user holds
Invora.*project roles. This is what makes calls succeed rather than 403.
Step 2 is not automatic. Nothing grants a new machine user any Invora permission by default.
The decided direction¶
Invora has decided that M2M authorization should be per-tenant self-service:
a dashboard action on the machine user creates the grant directly, with nobody at
Invora in the loop. That is the target, and the first release of it — Generate
client secret and Request API access on the Developer page — has shipped to
staging (invora-flutter#257).
It is not finished, and the honest limits today are:
- It is not yet on production — the release is on the staging line only.
- The Developer page can only be opened by an organization owner
(
invora-flutter#260), so a non-owner developer cannot use it. - Request API access can only grant roles your organization has already been delegated. If your organization has none, the dialog tells you so and stops — the delegation itself is an Invora-side step.
Where an Invora-side step is still required¶
Two enablement steps have no customer-facing route at all today:
- Delegating
Invora.*roles to your organization, which is the precondition for Request API access to have anything to offer. - Adding a machine user to the platform's trusted-resolver allowlist. This
is a hand-maintained, per-environment configuration value in Invora's internal
deployment repository; a machine user listed there is granted every
Invora.*permission without a per-organization role grant. It is deliberately reserved for Invora's own platform services and test clients — it is not the route a customer credential takes, it is not requestable, and it requires a commit by the Invora team. It is mentioned here only so it is clear that it exists and is not something you are missing.
Both go through the support route below.
Your tenant does not need an allowlist entry to be resolved
A machine user whose home organization is an Invora-provisioned tenant has
its tenant resolved from the organization id directly, with no allowlist
entry involved. Tenant resolution and permission granting are separate
gates — resolving fine and still getting PERMISSION_DENIED means you are
missing roles, not an allowlist entry.
Getting unblocked¶
There is one route today, and it is email:
-
Enablement requests (regulation enablement, role delegation), and anything on this page marked No or Partial.
To get a useful answer on the first reply, include:
- the environment — production or staging;
- your organization / tenant id;
- for an access problem, the machine user involved and the exact error
(
invalid_client,PERMISSION_DENIED, an HTTP status); - the capability from the table above that you are trying to obtain.
What we do not publish yet
There is no status page, no public issue tracker, and no documented response time for the address above. If you need a support commitment as part of a contract, raise it with your Invora contact — this page does not create one.
Related¶
- Getting Started — base URLs, protocol choice, and your first authenticated request.
- Authentication — the OIDC flows in full, scopes, introspection, and token handling.
- Quickstart — token to first invoice, end to end.
- Multi-Tenancy — connected businesses, org switching, and platform models.
- ZATCA integration — regulation onboarding and submission.
- Webhooks — endpoint registration, signatures, and delivery guarantees.
- Error Handling — what
UNAUTHENTICATEDandPERMISSION_DENIEDactually mean here.