Run your first guarded request
This page walks through one customer-facing request flow using a Stripe-style payment example. The goal is to show the fields you send, the responses you must handle, and where your team looks next when a request is approved, denied, or paused for review.
Use this page when you are ready to send your first real request to Ledgix after creating an API key and uploading policy content.
The request you send
Ledgix needs the exact tool name and the exact arguments you plan to execute. Keep the payload focused on the real action, not a generic plan or summary.
| Field | Type | Required | Description |
|---|---|---|---|
| tool_name | string | Yes | The action Ledgix is authorizing, such as create_stripe_payment or stripe_refund. |
| tool_args | object | Yes | The arguments that will be used if the action is allowed. |
| agent_id | string | Yes | The agent or service identity you want recorded with the request. |
| session_id | string | No | A correlation ID for the broader workflow or user session. |
| context.policy_id | string | No | Optional hint for the policy you want Ledgix to apply first. |
POST /request-clearance HTTP/1.1
Host: vault.example.com
Content-Type: application/json
X-Vault-API-Key: sk_prod_example
{
"tool_name": "create_stripe_payment",
"tool_args": {
"amount": 249.99,
"currency": "USD",
"customer_id": "cus_123",
"payment_method_id": "pm_123",
"order_event_id": "ord_evt_2048",
"reasoning": "Charge matches a completed order event."
},
"agent_id": "payments-agent",
"session_id": "checkout-42",
"context": {
"policy_id": "payments-prod"
}
}The responses you must handle
Ledgix returns one of four statuses:
| Field | Type | Required | Description |
|---|---|---|---|
| approved | terminal | Yes | The action is allowed. An approval token is included when the response is approved. |
| denied | terminal | Yes | The action should not run. Use the reason for logs, UI, or operator feedback. |
| pending_review | non-terminal | Yes | The request needs a human reviewer before it can continue. |
| processing | non-terminal | Yes | Ledgix accepted the request but has not returned a final answer yet. |
Example approved response:
{
"status": "approved",
"approved": true,
"requires_manual_review": false,
"token": "eyJhbGciOiJFZERTQSIsImtpZCI6InZhdWx0LWtleS0xIn0...",
"reason": "The payment matches the uploaded policy and the threshold.",
"request_id": "8ee2d480-4e23-49c5-9869-a0247e806e1c",
"confidence": 0.93,
"minimum_confidence_score": 0.80
}Example pending-review response:
{
"status": "pending_review",
"approved": false,
"requires_manual_review": true,
"token": null,
"reason": "The action may be valid, but the confidence score is below the current threshold.",
"request_id": "8ee2d480-4e23-49c5-9869-a0247e806e1c",
"confidence": 0.74,
"minimum_confidence_score": 0.80
}Polling when the result is not final
If you call the HTTP API directly, poll GET /clearance-status/{request_id} until the request resolves to approved or denied.
GET /clearance-status/8ee2d480-4e23-49c5-9869-a0247e806e1c HTTP/1.1
Host: vault.example.com
X-Vault-API-Key: sk_prod_exampleBoth SDKs already handle polling for processing and pending_review for you. Use the direct status endpoint only when you want custom behavior around long-running or human-reviewed requests.
Using the approval token
When the request is approved, Ledgix returns a short-lived token for that specific action. Most teams either forward that token to a protected gateway or attach it to the downstream call they are protecting.
if (clearance.approved && clearance.token) {
await fetch(process.env.STRIPE_GATEWAY_URL!, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${clearance.token}`,
},
body: JSON.stringify({
amount: 249.99,
customer_id: "cus_123",
}),
});
}The gateway then injects X-Ledgix-* headers upstream. See /security/cryptography-and-tokens for the security model.
Where the ledger write happens
For synchronous terminal outcomes, Vault writes the decision to the tenant ledger before returning success. That write contains:
- the request identifiers
- the tool name
- the tool argument payload
- the reason, citations, and evidence chunks
- the confidence score
- the approval outcome
- the computed
event_hash - the computed
leaf_hash - the signed receipt payload and signature
The event is durable at that point, but it may not yet have a leaf_index or checkpoint_id. Those arrive when the background sealer sequences the event into the Merkle tree.
Verifying the same request later
Once the event is sealed, you can fetch the full proof bundle:
GET /ledger/proof/bundle?request_id=8ee2d480-4e23-49c5-9869-a0247e806e1c HTTP/1.1
Host: vault.example.com
X-Vault-API-Key: sk_prod_exampleThe bundle includes:
- the full event record
- the inclusion proof
- an optional consistency proof to a later checkpoint
- the relevant verification keys
That is the artifact you want for customer-side or auditor-side offline verification.
Full example with the SDKs
import { configure, autoInstrument, currentToken, currentClearance } from "ledgix-ts";
import { LedgixClient } from "ledgix-ts";
const rawTools = {
async createStripePayment(amount: number, customerId: string, paymentMethodId: string) {
const token = currentToken(); // A-JWT for this approval
const clearance = currentClearance(); // Full ClearanceResponse if needed
return { authorized: true, token, requestId: clearance?.requestId };
},
};
// Configure once at startup
configure({ agentId: "stripe-agent-demo" });
// Instrument the module based on ledgix.json
const tools = autoInstrument(rawTools);
// Call the instrumented tool
const result = await tools.createStripePayment(249.99, "cus_farhan_approved_2048", "pm_demo_visa_4242");
// Verify the ledger proof after the fact
const client = new LedgixClient();
const bundle = await client.fetchLedgerProofBundle(result.requestId!);
const verification = await client.verifyLedgerProofBundle(bundle);
console.log(verification);How it fails
Where your team looks next
- Approved or denied requests show up in the customer ledger and advanced verification APIs.
- Pending-review requests appear in the customer dashboard review queue.
- Reviewers can approve or deny a paused request from the dashboard or by calling the review decision API.
Practical handling rules
- Treat
pending_reviewas an expected workflow state, not a transport error. - Log
request_idon every outcome so your team can trace a request in the dashboard later. - Keep the tool arguments stable between approval and execution. If the payload changes, request clearance again.
- If you use the SDK wrappers, test one denied request on purpose so you know how your app behaves when Ledgix blocks the action.