curl --request GET \
--url https://api.citadel.example/api/v1/approvals \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.citadel.example/api/v1/approvals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.citadel.example/api/v1/approvals"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.citadel.example/api/v1/approvals"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"data": [
{
"id": "0199a3c6-1d2e-7f30-8a41-5b6c7d8e9f01",
"kind": "transaction",
"operation_id": "0199a3c4-8a00-7c22-8d3e-4f5a6b7c8d9e",
"status": "pending",
"required": 2,
"reasons": [
"Amount over $5,000"
],
"group": "Treasury",
"rule_version": 3,
"requested_at": "2026-10-08T12:00:00Z",
"expires_at": "2026-10-09T12:00:00Z",
"decided_at": null,
"decisions": [
{
"decision": "approve",
"at": "2026-10-08T12:05:00Z",
"by": {
"id": "0199a3a2-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"name": "Ann Lee",
"email": "[email protected]"
}
}
]
},
{
"id": "0199a3c6-2e3f-7041-9b52-6c7d8e9f0a12",
"kind": "run",
"run_id": "0199a3d0-4b5c-7d6e-8f70-8192a3b4c5d6",
"workflow": "Large payout",
"title": "Pay 9,000 USDT to user-4211",
"message": null,
"status": "pending",
"required": 1,
"reasons": [],
"group": "Finance",
"rule_version": null,
"requested_at": "2026-10-08T11:30:00Z",
"expires_at": "2026-10-09T11:30:00Z",
"decided_at": null,
"decisions": []
}
],
"next_cursor": null
}List approvals
What waits, or waited, for people on one network, newest first: transactions your approval
rules held for a passkey quorum (kind: transaction), and workflow runs stopped at a manual
approval step (kind: run). With status=pending, what waits now and until when
(expires_at). Approvals of changes made in the console (approval rules, workflows) are
not listed.
Read only. People approve or reject in the console, each with their own passkey; an API
key can neither approve nor reject. A pending approval past expires_at reads, and is
filtered as, expired. A transaction’s row carries the approval block that
GET /transactions/{id} shows; a run’s row names its workflow and the step’s title and
message.
network is required. Paged with a cursor — see Lists and pagination. Needs the read
scope. A key limited to vaults lists the transactions from wallets in them only, and no
runs: a workflow chooses its own wallets.
curl --request GET \
--url https://api.citadel.example/api/v1/approvals \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.citadel.example/api/v1/approvals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.citadel.example/api/v1/approvals"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.citadel.example/api/v1/approvals"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"data": [
{
"id": "0199a3c6-1d2e-7f30-8a41-5b6c7d8e9f01",
"kind": "transaction",
"operation_id": "0199a3c4-8a00-7c22-8d3e-4f5a6b7c8d9e",
"status": "pending",
"required": 2,
"reasons": [
"Amount over $5,000"
],
"group": "Treasury",
"rule_version": 3,
"requested_at": "2026-10-08T12:00:00Z",
"expires_at": "2026-10-09T12:00:00Z",
"decided_at": null,
"decisions": [
{
"decision": "approve",
"at": "2026-10-08T12:05:00Z",
"by": {
"id": "0199a3a2-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"name": "Ann Lee",
"email": "[email protected]"
}
}
]
},
{
"id": "0199a3c6-2e3f-7041-9b52-6c7d8e9f0a12",
"kind": "run",
"run_id": "0199a3d0-4b5c-7d6e-8f70-8192a3b4c5d6",
"workflow": "Large payout",
"title": "Pay 9,000 USDT to user-4211",
"message": null,
"status": "pending",
"required": 1,
"reasons": [],
"group": "Finance",
"rule_version": null,
"requested_at": "2026-10-08T11:30:00Z",
"expires_at": "2026-10-09T11:30:00Z",
"decided_at": null,
"decisions": []
}
],
"next_cursor": null
}Authorizations
An API key minted in the console (ctd_…). Authenticates; never authorizes funds. A key
carries scopes (read, wallets.write, transactions.write, events.write,
webhooks.write, policy.write) and may be limited to vaults, to IP addresses, and in
lifetime — see Authentication.
Query Parameters
The network the data lives on. Required — there is no default network: without it,
422 network_required. A wallet named by its id on another network: 422 network_mismatch.
The network the data lives on. Required wherever it appears in a request — there is no
default network. One your installation does not serve: 422 network_not_served.
mainnet, testnet One status, or several separated by commas (approved,rejected).
"pending"
transaction or run, or both separated by a comma.
"transaction"
Only rows Citadel recorded at this time or later (created_at). An ISO 8601 date-time.
Only rows Citadel recorded before this time (created_at). An ISO 8601 date-time.
Rows per page, 1 to 100. Out of range, 422.
1 <= x <= 100The next_cursor of the page before; absent for the first page. Send the same filters
with it. A cursor this API did not issue answers 422 invalid_cursor.