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:read to read policies and versions, policy:create to create a draft, and policy:version:create to add a version. policy:update covers metadata changes, activation, selecting the current version and archiving. Administrators can assign these permissions to different roles; the operations covered by policy:update share 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 instructionCheck
Below, at and above a thresholdThe boundary operators select the intended rules and currency.
Matches multiple approval rulesAll expected tasks exist and higher steps remain gated.
Matches a refusal and approval ruleRefusal wins.
Matches no ruleThe configured unmatched outcome is applied; manual review has a recovery path.
Approved or rejected by an eligible userThe request resolves and the transaction reflects the decision.
Attempted by the initiator or an unrelated userThe 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.0

The 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 wantWrite
Automatic approvalemit.autoApproval: true
One approver or teamemit.approver: "#payments_manager"
Sequential sign-offTwo rules with emit.step: 0 and emit.step: 1
A hard ceilingemit.outcome: REJECT. A refusal always outranks approval.
Behaviour when nothing matchesdefaults.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 actionContext fieldsTypes
MINT, BURN, TRANSFER, FIAT_PAYMENT, INTERNATIONAL_TRANSFERamount, assetIdAMOUNT, STRING
FIAT_TOKEN, TOKEN_FIAT, FX_CONVERSIONamount, assetId, targetAssetIdAMOUNT, 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.

  1. Create a new version with a note of what changed. The policy keeps serving its current version.
  2. 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, action and templateKey slot, 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

Errors and edge cases

SituationResponseWhat to do
content names a field outside the action's catalogue, uses !=, or compares amount without assetId400 with VALIDATION_FAILEDCorrect the rule and resend
templateKey names no template visible to your organisation404List templates and use a visible key
Activating a second policy for the same ruleType, action and templateKey409Archive the other policy first
Setting a current version that does not exist404List versions with GET /api/policies/{policyId}/versions
A rule names a field the event does not supplyThe evaluation ends with an errorSend every field any rule names
  • Archiving has no state guard. A DRAFT or already ARCHIVED policy can be archived.
  • hitPolicy is 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

Policies code recipes.

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.


Did this page help you?