POST /api/zsc/grant/:name
Add a ZID to the secret's permissions[] array. The granted ZID
then passes the permission gate when calling ZeqContext.read().
Idempotent — granting an already-permitted ZID is a no-op.
The owner (bound_zid) and the system bypass (ZEQ-SYS) are always
permitted; you only grant additional ZIDs through this endpoint.
Auth
Admin cookie (zeq_admin) required.
Path parameters
| Param | Type | Notes |
|---|---|---|
name | string | The vault key. |
Request
curl -H "Cookie: zeq_admin=$ADMIN_JWT" \
-H "Content-Type: application/json" \
-X POST https://YOUR-FRAMEWORK/api/zsc/grant/STRIPE_SECRET_KEY \
-d '{ "zid": "ZEQ07111111111" }'
Body fields
| Field | Required | Type | Notes |
|---|---|---|---|
zid | yes | string | The ZID to add to permissions[]. |
Response · 200 OK
{
"ok": true,
"name": "STRIPE_SECRET_KEY",
"zid": "ZEQ07111111111",
"permissions": ["ZEQ07111111111", "ZEQ07222222222"]
}
permissions is the complete updated list after the grant —
useful for confirming the operation without a separate /info call.
Errors
| Status | error | Cause |
|---|---|---|
400 | name + zid required | Missing path segment or body field. |
401 | unauthorized | Admin cookie missing/invalid. |
404 | NOT_FOUND | No row with this name. |
500 | INTERNAL_ERROR | DB error. |
Audit row
transition_type = "secret_set"
actor_zid = <the admin's ZID>
payload_json = { name, purpose: "granted", granted_zid: <zid>, permissions: [...] }
proof_digest = SHA-256(name | actor_zid | transition_id | "granted")
Grants are recorded as secret_set rows with purpose='granted' so
the permission history is itself audit-chained — see ZSC Audit Trail.
Related
POST /api/zsc/revoke/:name— remove a ZID from permissionsPOST /api/zsc/probe-permission— verify a grant took effect without exposing plaintext- Permissions reference — the gate's full algorithm