APIZ guide
Structured Policy Tutorial
Build a General API Policy that allows GET and HEAD requests and denies
every write method. The tutorial uses the built-in General API read only
project and two credential-free Fixtures.
WHEN action is http:get or http:head
THEN allow with reason read_request_allowed
OTHERWISE deny with reason write_request_not_allowed
Complete either the Web Console or CLI path. Both publish the same Policy Project contract.
Before You Start
- Create a General API Instance and bind it to a Client.
- Keep the Client Binding private.
- Use synthetic Fixture paths. They do not need to exist on the upstream API.
- For the final live check, choose a real read path and a harmless write request suitable for your own test upstream.
Web Console
1. Create From The Structured Example
- Open Policies and select Create Policy.
- Choose Structured.
- Select the General API adapter scope.
- Under Start from an example, choose General API read only.
- Review the name, description, and two Fixtures, then select Create from example.
Creation produces an inactive Policy and a Working Draft. It does not affect traffic, contact the upstream API, or attach itself.
2. Inspect The Guided Definition
Policy Studio opens structured.policy in Form view. Confirm:
- WHEN contains actions
http:getandhttp:head; - THEN is
allowwith reasonread_request_allowed; - OTHERWISE is
denywith reasonwrite_request_not_allowed.
Use YAML or JSON only when you need the complete portable definition. After editing source view, select Apply source before Save, Run, or Review publication. Invalid or unapplied source cannot silently leave an older definition active.
3. Inspect And Run The Draft Fixtures
In the Studio Explorer, open both Fixtures:
- Allow a GET request sends synthetic
GET /v1/itemsand expectsallow; - Deny a POST request sends synthetic
POST /v1/itemsand expectsdeny.
Fixtures contain no credentials and do not reach the upstream service. Select Run, then Run all enabled Fixtures. The Studio saves the exact in-memory Draft when needed, validates it, runs both cases, and opens the result panel.
Do not continue until both Fixtures pass and their actual reasons match the expectations.
4. Publish And Establish Readiness
- Select Review publication. APIZ saves and compiles as needed. Review the changes and traffic effect, then confirm the next Published Revision.
- On Policy detail, open Run tests.
- Select Run full suite.
- Confirm Published suite passed. Readiness is stored against this exact Published Revision and content hash.
- Return to Policy detail or the list and select Enable Policy.
Publishing promotes definition and Fixtures atomically. It does not enable or attach the Policy.
5. Attach To One Client Binding
- Open Clients and select the Client used for this tutorial.
- Find its General API Binding under API Access.
- Expand Policies.
- Choose the new Policy and select Attach Policy.
- Keep it required with execution order
100.
Active Policies appear regardless of test state. Testing is optional; notices distinguish absent, failed, and outdated evidence without blocking attachment.
6. Verify Allow, Deny, And Evidence
Issue a short-lived Temporary Credential for the Binding, then send:
- a safe
GETto a real read path: it should pass Policy and reach upstream; - a harmless
POSTor other write request: APIZ should deny it before Secret Broker release and before upstream traffic.
Open Logs / Audit → Access Logs, filter by the Client, and compare the
two rows. The denied request should show write_request_not_allowed; the
allowed request should show the upstream result. Neither row may contain a
Temporary Credential or upstream credential value.
CLI
1. Create The Policy Project
Create this directory outside a repository that might accidentally commit runtime secrets:
general-read-only/
├── apiz-policy.json
└── fixtures/
├── allow-get.json
└── deny-post.json
apiz-policy.json:
{
"api_version": "apiz.io/v1",
"kind": "PolicyProject",
"metadata": {
"name": "General API read only",
"description": "Allow GET and HEAD requests while denying every write method."
},
"config": {
"type": "structured",
"adapter_scope": "general",
"definition": {
"when": { "actions": ["http:get", "http:head"] },
"then": { "decision": "allow", "reason": "read_request_allowed" },
"otherwise": { "decision": "deny", "reason": "write_request_not_allowed" }
}
},
"code": null,
"fixtures": [
{ "file": "fixtures/allow-get.json" },
{ "file": "fixtures/deny-post.json" }
]
}
fixtures/allow-get.json:
{
"metadata": {
"key": "allow-get",
"name": "Allow a GET request",
"enabled": true,
"adapter_id": "general"
},
"request": { "method": "GET", "path": "/v1/items" },
"expectation": { "decision": "allow", "reason": "read_request_allowed" }
}
fixtures/deny-post.json:
{
"metadata": {
"key": "deny-post",
"name": "Deny a POST request",
"enabled": true,
"adapter_id": "general"
},
"request": { "method": "POST", "path": "/v1/items" },
"expectation": { "decision": "deny", "reason": "write_request_not_allowed" }
}
Fixtures must remain synthetic and credential-free.
2. Publish The Project
The CLI first saves and compiles a Structured project into a reviewed Working
Draft. It does not currently run a Structured project locally with
policy test --project.
umask 077
apiz -o json policy publish --project ./general-read-only \
> structured-policy-review.json
POLICY_ID=$(jq -r '.candidate.policy_id' structured-policy-review.json)
DRAFT_REVISION=$(jq -r '.candidate.draft_revision' structured-policy-review.json)
POLICY_REVISION=$(jq -r '.candidate.current_published_revision' structured-policy-review.json)
jq '{candidate, changes, evidence, traffic_effect}' structured-policy-review.json
apiz -o json policy publish "$POLICY_ID" --project ./general-read-only \
--expected-draft-revision "$DRAFT_REVISION" \
--expected-policy-revision "$POLICY_REVISION" \
--confirm > structured-policy-publish.json
When no Policy id is supplied, policy publish creates an inactive Policy,
saves the Working Draft, performs authoritative validation/compile, and returns
the review without publishing. The confirmation publishes only the exact
reviewed definition and Fixtures. To update the same Policy, prepare another
review using its id and the current Draft revision shown by policy draft show:
apiz policy publish "$POLICY_ID" --project ./general-read-only \
--expected-draft-revision <current-draft-revision>
3. Run The Published Suite And Enable
apiz policy test-suite "$POLICY_ID"
apiz policy enable "$POLICY_ID"
The published suite is recommended evidence for this tutorial, not a publish or attachment gate. If it is absent or failed, Web and CLI should warn about the exact evidence state while still allowing an authorized attachment.
4. Attach To The Client Binding
apiz client binding policy add <client-binding-id> \
--policy "$POLICY_ID" \
--order 100
The binding is required by default. Do not add --optional for this tutorial.
5. Verify And Inspect Evidence
Use a Temporary Credential for the bound Client to send one safe read and one harmless write request. Then inspect:
apiz access-log list --client <client-id> --since 15m --limit 50
apiz access-log request <request-id>
Confirm that the read reached upstream, the write was denied with
write_request_not_allowed, and the denied request did not release the
upstream credential.
Change Or Remove The Policy
- Edit and republish a new revision before changing live behavior.
- Re-run the full published suite after every publish.
- Disable or remove the Policy Binding to stop applying it to this route.
- Disabling a Policy is different from revoking the Client's Temporary Credential or rotating the upstream credential.
Next: Scripted Policy tutorial.