Policies
6 operations under /v1/policies (GET POST PATCH DELETE): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/policies
https://api.codespar.dev/v1/policiesList policy rules for the authenticated org, sorted by order ASC.
Responses
| Status | Body | Description |
|---|---|---|
200 | array of Policy | OK |
curl -X GET https://api.codespar.dev/v1/policies \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/policies HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/policies",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/policies", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/policies");[
{
"id": "pol_0000000000000000",
"name": "Example",
"type": "deny",
"config": {},
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true,
"order": 1,
"createdAt": "2026-01-15T12:00:00.000Z",
"updatedAt": "2026-01-15T12:00:00.000Z"
}
]POST /v1/policies
https://api.codespar.dev/v1/policiesCreate a new policy rule. A budget rule counts DECLARED intent, not money that left: the cost is added to the agent's daily and monthly counters when the call is evaluated, before anything is sent (the payment amount on consumer payments; for an agent's codespar_pay, the amount of the call; for any other tool on session execute, the caller's estimatedCost). The counters are kept in major units of BRL: a codespar_pay of amount 2500 in BRL counts 25, and the estimatedCost a caller declares beside a codespar_pay is not counted. A codespar_pay in another currency, or with no amount, is allowed and counts nothing, and so does every other outbound meta-tool; the API logs budget rule matched without derivable cost each time a budget rule lets one through. A consumer payment that provably moved nothing (refused before dispatch, or refused by the provider) gives its cost back; one whose outcome is uncertain stays counted. A codespar_pay counted through the meta-tool path is not given back when it fails.
Request body — PolicyCreate
| Field | Type | Required | Description |
|---|---|---|---|
agents | array of string | no | — |
config | — | no | — |
enabled | boolean | no | — |
name | string | yes | — |
tools | array of string | no | — |
type | "deny" | "budget" | "rate-limit" | "time-window" | "approval-required" | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | Policy | OK |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
agents | array of string | yes | — |
config | object | yes | — |
createdAt | string (date-time) | yes | — |
enabled | boolean | yes | — |
id | string | yes | — |
name | string | yes | — |
order | number | yes | — |
tools | array of string | yes | — |
type | "deny" | "budget" | "rate-limit" | "time-window" | "approval-required" | yes | — |
updatedAt | string (date-time) | yes | — |
curl -X POST https://api.codespar.dev/v1/policies \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Example",
"type": "deny",
"agents": [],
"tools": [],
"enabled": true
}'POST /v1/policies HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"name": "Example",
"type": "deny",
"agents": [],
"tools": [],
"enabled": true
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/policies",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"name": "Example",
"type": "deny",
"agents": [],
"tools": [],
"enabled": True
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/policies", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Example",
"type": "deny",
"agents": [],
"tools": [],
"enabled": true
}),
});
const data = await res.json();const result = await cs.api.post("/v1/policies", {
body: {
name: "Example",
type: "deny",
agents: [],
tools: [],
enabled: true
}
});{
"id": "pol_0000000000000000",
"name": "Example",
"type": "deny",
"config": {},
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true,
"order": 1,
"createdAt": "2026-01-15T12:00:00.000Z",
"updatedAt": "2026-01-15T12:00:00.000Z"
}POST /v1/policies/reorder
https://api.codespar.dev/v1/policies/reorderReorder policy rules. Atomic: if any id does not belong to the org, the entire operation rolls back and returns 404.
Request body — PolicyReorder
| Field | Type | Required | Description |
|---|---|---|---|
ids | array of string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
204 | — | No Content |
404 | object | Not Found |
curl -X POST https://api.codespar.dev/v1/policies/reorder \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"string"
]
}'POST /v1/policies/reorder HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"ids": [
"string"
]
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/policies/reorder",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"ids": [
"string"
]
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/policies/reorder", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"ids": [
"string"
]
}),
});
const data = await res.json();const result = await cs.api.post("/v1/policies/reorder", {
body: {
ids: [
"string"
]
}
});GET /v1/policies/{id}
https://api.codespar.dev/v1/policies/{id}Read one policy rule
The same row the list returns, by id. 404 when the id belongs to another org: the org predicate excludes it from the query, so the handler cannot tell 'other tenant' from 'absent' — and must not, because telling them apart leaks existence.
Responses
| Status | Body | Description |
|---|---|---|
200 | Policy | OK |
404 | object | Not Found |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
agents | array of string | yes | — |
config | object | yes | — |
createdAt | string (date-time) | yes | — |
enabled | boolean | yes | — |
id | string | yes | — |
name | string | yes | — |
order | number | yes | — |
tools | array of string | yes | — |
type | "deny" | "budget" | "rate-limit" | "time-window" | "approval-required" | yes | — |
updatedAt | string (date-time) | yes | — |
curl -X GET https://api.codespar.dev/v1/policies/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/policies/{id} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/policies/{id}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/policies/{id}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/policies/{id}");{
"id": "pol_0000000000000000",
"name": "Example",
"type": "deny",
"config": {},
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true,
"order": 1,
"createdAt": "2026-01-15T12:00:00.000Z",
"updatedAt": "2026-01-15T12:00:00.000Z"
}PATCH /v1/policies/{id}
https://api.codespar.dev/v1/policies/{id}Update a policy rule. Cross-org ids return 404. Invalidates the per-org cache on success.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Request body — PolicyUpdate
| Field | Type | Required | Description |
|---|---|---|---|
agents | array of string | no | — |
config | — | no | — |
enabled | boolean | no | — |
name | string | no | — |
tools | array of string | no | — |
type | "deny" | "budget" | "rate-limit" | "time-window" | "approval-required" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | Policy | OK |
404 | object | Not Found |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
agents | array of string | yes | — |
config | object | yes | — |
createdAt | string (date-time) | yes | — |
enabled | boolean | yes | — |
id | string | yes | — |
name | string | yes | — |
order | number | yes | — |
tools | array of string | yes | — |
type | "deny" | "budget" | "rate-limit" | "time-window" | "approval-required" | yes | — |
updatedAt | string (date-time) | yes | — |
curl -X PATCH https://api.codespar.dev/v1/policies/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Example",
"type": "deny",
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true
}'PATCH /v1/policies/{id} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"name": "Example",
"type": "deny",
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true
}import os
import requests
res = requests.patch(
"https://api.codespar.dev/v1/policies/{id}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"name": "Example",
"type": "deny",
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": True
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/policies/{id}", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Example",
"type": "deny",
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true
}),
});
const data = await res.json();const result = await cs.api.patch("/v1/policies/{id}", {
path: {
id: "policie_0000000000000000"
},
body: {
name: "Example",
type: "deny",
agents: [
"string"
],
tools: [
"string"
],
enabled: true
}
});{
"id": "pol_0000000000000000",
"name": "Example",
"type": "deny",
"config": {},
"agents": [
"string"
],
"tools": [
"string"
],
"enabled": true,
"order": 1,
"createdAt": "2026-01-15T12:00:00.000Z",
"updatedAt": "2026-01-15T12:00:00.000Z"
}DELETE /v1/policies/{id}
https://api.codespar.dev/v1/policies/{id}Delete a policy rule. Cross-org ids return 404. Invalidates the per-org cache on success.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
204 | — | No Content |
404 | object | Not Found |
curl -X DELETE https://api.codespar.dev/v1/policies/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY"DELETE /v1/policies/{id} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.delete(
"https://api.codespar.dev/v1/policies/{id}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/policies/{id}", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.delete("/v1/policies/{id}", {
path: {
id: "policie_0000000000000000"
}
});Approvals
5 operations under /v1/approvals (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Policy Evaluations
1 operation under /v1/policy-evaluations (GET): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.