Your own permissions — define them in Zynth, and let Zynth decide
Supported today. You can define your application's own authorization vocabulary in Zynth — resources, permissions and roles — and have Zynth answer "may this principal do this?" for your software. You do not need to build or run a parallel entitlements service alongside Zynth.
If you are evaluating Zynth and planning to keep your own authorization model because you assumed Zynth only knows about its own permissions, read this page first. That assumption is the most common one we see, and it is wrong.
How it fits together
- A resource — your application or API, bound one-to-one to an OAuth resource you have already registered. It supplies a namespace.
- Permissions —
<namespace>:<action>, e.g.billing:invoices:void. - Roles — your own roles, which can combine your permissions with Zynth's.
- Decisions — delivered two ways: as claims in an access token minted for your API, or live from the check API.
One engine answers everything. The same evaluator that enforces Zynth's own permissions decides yours. Assignments, delegations, attribute conditions and "why does this person have access" explanations all work on your permissions with no separate code path.
1. Register your resource
Register the OAuth resource first; an authorization resource binds to an audience Zynth already knows. Then:
curl -X POST https://auth.zynthmedia.com/api/v1/authz/resources \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"oauth_resource_id": "7c0e3f2a-…",
"namespace": "billing",
"description": "The billing API"
}'
Returns 201 with resource_id and namespace.
| Rule | What happens |
|---|---|
Namespace is 2–40 characters of a-z, 0-9 and -, starting with a letter | Otherwise 422 |
| Namespace may not be one of Zynth's own permission domains | 409 — your vocabulary can never collide with the platform's |
| The OAuth resource must exist | 422 — "register the OAuth resource first" |
| One OAuth resource ↔ one namespace | 409 if either is already bound |
Who can do this: a human holding authz:resources:manage. An agent or an API key cannot
define your authorization vocabulary, by design — the people who decide what a permission means
are people.
2. Define permissions
curl -X POST https://auth.zynthmedia.com/api/v1/authz/resources/$RESOURCE_ID/permissions \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "invoices:void",
"description": "Void an issued invoice",
"read_only": false,
"risk": 12
}'
This creates billing:invoices:void.
| Field | Meaning |
|---|---|
action | The part after the namespace, 1–60 characters |
read_only | Marks a read. Reads and writes are treated differently in risk scoring and in agents' write budgets |
risk | 1–15. Optional; defaults low for reads and higher for writes. Feeds risk-based decisions and review |
List what you have with GET /api/v1/authz/resources (needs authz:resources:read).
3. Grant them through roles
Your permissions go into roles exactly like Zynth's own — the existing role API accepts them
side by side. See Authorization admin for
POST /api/v1/authz/roles.
From there the rest of Zynth's authorization applies unchanged: assignment to users, delegation to agents, attribute conditions, and the "why does this principal have access" explanation.
4. Ask Zynth to decide
Option A — read the claims in an access token (fast)
Mint an access token for your API (RFC 8707 resource), and it carries:
roles— the principal's role codes;permissions— their effective permissions, filtered to your namespace.
Verify the token locally against Zynth's JWKS and read the claims. No network call per request.
Three rules to build against:
- Past 96 permissions, the list is omitted — not shortened. The token then carries
"permissions_truncated": trueand nopermissionslist. A partial list would be a fail-open authorization statement, so Zynth never sends one. When you see the flag, ask the check API. - No vocabulary, no claims. A resource you have registered but not given permissions yields no
rolesorpermissionsat all. Nothing is invented. - Claims are as fresh as the token. A role change reaches the claims at the next refresh — up to the access token's lifetime (15 minutes by default). For a decision that must be current now, use the check API.
See verifying tokens → resource claims.
Option B — the check API (current, and batchable)
| Endpoint | Who calls it | Asks about |
|---|---|---|
POST /api/v1/authz/check | Any principal, with its own token | Itself only — naming another principal answers 403 |
POST /api/v1/authz/service-check | Your resource server, with a client_credentials token carrying scope authz:check | Any principal in its tenant — a member, or an agent (answered from its delegations and oversight band) |
curl -X POST https://auth.zynthmedia.com/api/v1/authz/service-check \
-H "Authorization: Bearer $SERVICE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"checks": [
{"permission": "billing:invoices:void", "principal_id": "3f9a…"}
]}'
Up to 50 checks per call (more answers 422). Each result:
{
"principal_id": "3f9a…",
"permission": "billing:invoices:void",
"allowed": true,
"via": "rbac",
"reason": "…",
"obligations": []
}
A check is a question, not an act: it changes nothing and charges nothing. Denials are recorded as security telemetry, and the endpoint is monitored as an enumeration surface — probing it for what a principal could do is itself a signal.
⚠️
allowed: trueis not always "go"If
obligationsis non-empty, treat the answer as "pending". An obligation such ashuman_approvalmeans the action is permitted only once a human approves it — for example an agent under human-in-the-loop oversight. Zynth surfaces the obligation; it never resolves it inside a check. Proceed only whenallowedistrueandobligationsis empty.A resource server that reads
allowedalone will proceed on actions that are still waiting for a human. See agent action approvals.
A check never parks an approval. To have Zynth decide an action and park a human approval for it — for an action your own executor performs — use the act endpoint, which takes one of the permissions you defined here: Approvals for actions Zynth does not execute.
5. Removing things
DELETE /api/v1/authz/resources/permissions/{permission_id}removes one permission.DELETE /api/v1/authz/resources/{resource_id}removes a resource — and is refused with409while any role still grants one of its permissions. Remove it from the roles first. Zynth will not silently strip authority out of roles people are relying on.
What this is not
- Not a billing meter. A permission can gate an action; it does not count usage. If you meter LLM tokens or spend, meter in your gateway and feed the verdict back as a permission — see Integrating an agent platform §5.
- Not a second evaluator. There is no separate "customer authorization engine" to keep in step with Zynth's. Your permissions and Zynth's go through the same decision, the same audit trail and the same explanations.