Author and simulate a policy
For risk, compliance and integration teams who write transaction policies, version them and put them live.
Who this is for
Risk, compliance and operations staff who set payment controls, and the integration engineers who automate policy changes. This page covers choosing a template, writing rules, creating a policy, putting it live, changing it safely and rolling it back. Policy, approvals and audit explains the model behind each step.
Before you start
- Policies are set by your own administrators, in the Management Panel or through the API. No software change or request to Quant Network is needed.
- Your role needs
policy:readto read policies and versions,policy:createto create a draft, andpolicy:version:createto add a version.policy:updatecovers metadata changes, activation, selecting the current version and archiving. Administrators can assign these permissions to different roles; the operations covered bypolicy:updateshare one permission. See Roles and access. - Inspect the organisation's policies and templates before submitting test operations. Confirm with the operator that policy evaluation is enabled for the operation types you intend to control; a newly created organisation does not itself prove a policy is enforced.
Prepare a control you can test
Write down the governed operation, currency thresholds, approver group keys, order of decisions, deadline and unmatched outcome before creating the policy. Agree what the application should show while approval is pending and who can recover an unassigned task. A rule document is only one part of that workflow.
Choose an existing template for the supported action and inspect its content. Confirm each group exists and has the intended independent users. Keep a version-change note that explains the business reason, then use controlled operations to check the following behaviour:
| Test instruction | Check |
|---|---|
| Below, at and above a threshold | The boundary operators select the intended rules and currency. |
| Matches multiple approval rules | All expected tasks exist and higher steps remain gated. |
| Matches a refusal and approval rule | Refusal wins. |
| Matches no rule | The configured unmatched outcome is applied; manual review has a recovery path. |
| Approved or rejected by an eligible user | The request resolves and the transaction reflects the decision. |
| Attempted by the initiator or an unrelated user | The attempted decision is refused. |
There is no external policy-simulation endpoint. These are controlled integration tests of an active policy, so isolate the accounts and instructions used for them.
1. Choose a template
Every policy starts from a template. List the templates your organisation can use:
GET /api/templates HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer <access token>
API-Version: 1.0.0The response holds SYSTEM templates, visible to every organisation, then your organisation's TENANT templates. Deprecated templates are left out unless you pass includeDeprecated=true.
Illustrative example built from the schema.
{
"data": [
{
"key": "PAYMENT_TIERS",
"scope": "SYSTEM",
"templateVersion": 1,
"name": "Payment approval tiers",
"action": "FIAT_PAYMENT",
"description": "Automatic approval below a threshold, approvers above it, refusal beyond a ceiling",
"status": "ACTIVE"
}
],
"page": 0,
"size": 20,
"totalItems": 1,
"totalPages": 1
}A template's defaultContent is not copied into a new policy. Supply the rules yourself when you create the policy. A policy is a copy, so a later change to the template does not change it.
You can also keep templates of your own with POST /api/templates. Their content is not validated when saved. Invalid rules are rejected when a policy is created from the template.
2. Write the rules
A policy's content names the action it governs in appliesTo.action and lists rules. Each rule has an id, a when block of conditions and an emit block saying what happens.
| You want | Write |
|---|---|
| Automatic approval | emit.autoApproval: true |
| One approver or team | emit.approver: "#payments_manager" |
| Sequential sign-off | Two rules with emit.step: 0 and emit.step: 1 |
| A hard ceiling | emit.outcome: REJECT. A refusal always outranks approval. |
| Behaviour when nothing matches | defaults.unmatched: REQUIRE_MANUAL_REVIEW (default) or AUTO_APPROVE |
Rules on amount must also constrain assetId, so every threshold is per currency. Name only fields in the action's context catalogue, and supply every field your rules reference.
Context fields
The catalogue is defined by the platform release, not by an individual template. The current catalogue allows these fields in when:
| Policy action | Context fields | Types |
|---|---|---|
MINT, BURN, TRANSFER, FIAT_PAYMENT, INTERNATIONAL_TRANSFER | amount, assetId | AMOUNT, STRING |
FIAT_TOKEN, TOKEN_FIAT, FX_CONVERSION | amount, assetId, targetAssetId | AMOUNT, STRING, STRING |
For conversion actions, amount and assetId describe the source side; targetAssetId describes the asset received. Use decimal strings for amounts and constrain the source assetId whenever a rule compares amount. Unknown fields and operators that do not suit a field's type are rejected when policy content is saved. See Policy, approvals and audit for the condition forms.
These are policy action names, not a list of transaction types accepted by POST /api/transactions. Having an action in the catalogue does not prove that its calling workflow is enabled in your environment. Confirm the deployed policy release and workflow configuration before testing enforcement.
3. Create the policy
POST /api/policies creates the policy in DRAFT at version 1. A draft is visible and editable, and affects no operation. See the worked example on Policy, approvals and audit for a full create request and response.
4. Test before going live
Simulation is not available through the API today. There is no operation that runs sample transactions through a draft policy. Test the policy with isolated accounts and controlled operations in a non-production environment. Activating a policy affects matching operations; there is no public dry-run endpoint.
5. Activate
POST /api/policies/{policyId}/activate moves the policy to ACTIVE. From then on, every matching event is checked against its current version. At most one non-archived policy may hold the same ruleType, action and templateKey. Avoid overlapping active policies until the operator has established which templates and actions apply. The public API does not define a precedence rule across different templates. Activating an ACTIVE policy again succeeds and changes nothing.
6. Change a live policy
A change is made in two steps, so authoring and going live stay separate.
- Create a new version with a note of what changed. The policy keeps serving its current version.
- Point the policy at the new version when you are ready.
Every version is numbered, immutable and records who created it and when.
7. Roll back or retire
- To roll back, point the policy at an earlier version number with the same call as step 6.2.
- To retire a policy, archive it with POST /api/policies/{policyId}/archive. It stops being selected, frees its
ruleType,actionandtemplateKeyslot, and keeps its versions.
Release a policy change with evidence
The bank owns the rule and approval design; Quant Flow validates the supported context and evaluates the selected active policy. Keep policy/version identifiers, current-version selection and group dependencies in the release record.
There is no external dry-run endpoint. Exercise boundary, rejection and approval cases with controlled non-production instructions, then inspect requests/tasks and transaction outcomes. Creating a version does not select it as current. Use Production readiness for acceptance and Daily operations and monitoring for pending decisions.
Worked example
Raise the automatic threshold of a live policy from 10,000 to 15,000 EUR.
Create version 2:
POST /api/policies/0b8e2f4a-6c13-4d7e-9a25-3f1c8d6e4b90/versions HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer <access token>
API-Version: 1.0.0
Content-Type: application/json
{
"changeSummary": "Automatic threshold raised to 15,000 EUR",
"content": {
"appliesTo": { "action": "FIAT_PAYMENT" },
"rules": [
{
"id": "automatic",
"when": { "amount": "[0.00 .. 15000.00)", "assetId": "== EUR" },
"emit": { "autoApproval": true, "reason": "Under 15,000 EUR" }
},
{
"id": "payments-manager",
"when": { "amount": ">= 15000.00", "assetId": "== EUR" },
"emit": { "step": 0, "approver": "#payments_manager", "reason": "15,000 EUR or more" }
}
],
"defaults": { "unmatched": "REQUIRE_MANUAL_REVIEW" }
}
}Illustrative example built from the schema.
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "5d1a7c30-2e84-4b6f-a913-8c0e2f4d6b17",
"policyId": "0b8e2f4a-6c13-4d7e-9a25-3f1c8d6e4b90",
"versionNumber": 2,
"content": {
"appliesTo": { "action": "FIAT_PAYMENT" },
"hitPolicy": "collect",
"rules": [
{
"id": "automatic",
"when": { "amount": "[0.00 .. 15000.00)", "assetId": "== EUR" },
"emit": { "autoApproval": true, "reason": "Under 15,000 EUR" }
},
{
"id": "payments-manager",
"when": { "amount": ">= 15000.00", "assetId": "== EUR" },
"emit": { "step": 0, "approver": "#payments_manager", "reason": "15,000 EUR or more" }
}
],
"defaults": { "unmatched": "REQUIRE_MANUAL_REVIEW" }
},
"changeSummary": "Automatic threshold raised to 15,000 EUR",
"createdBy": "8f14e45f-ceea-467a-9a1e-1c8b4b0b5a21",
"createdAt": "2026-09-02T10:15:00.000Z"
}The live policy still serves version 1. Put version 2 live:
PUT /api/policies/0b8e2f4a-6c13-4d7e-9a25-3f1c8d6e4b90/current-version HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer <access token>
API-Version: 1.0.0
Content-Type: application/json
{ "versionNumber": 2 }The response is the policy, now with "version": 2. To roll back, send { "versionNumber": 1 }.
View in API Reference
- Create a new version ·
POST- Set the current version ·
PUT
Errors and edge cases
| Situation | Response | What to do |
|---|---|---|
content names a field outside the action's catalogue, uses !=, or compares amount without assetId | 400 with VALIDATION_FAILED | Correct the rule and resend |
templateKey names no template visible to your organisation | 404 | List templates and use a visible key |
Activating a second policy for the same ruleType, action and templateKey | 409 | Archive the other policy first |
| Setting a current version that does not exist | 404 | List versions with GET /api/policies/{policyId}/versions |
| A rule names a field the event does not supply | The evaluation ends with an error | Send every field any rule names |
- Archiving has no state guard. A
DRAFTor alreadyARCHIVEDpolicy can be archived. hitPolicyis stored as sent and has no effect. Every matching rule contributes.
API Reference
Open an endpoint for request fields, response examples and the interactive API console.
Related pages
Implementation resources
Use Implement a backend client for request handling, durable idempotency and outcome tracking. The developer integration coverage map links every public operation to its API Reference and contract-derived code recipe. Replace example identifiers with values from your assigned environment.
Updated 4 days ago

