Implementation guides

Approvals

Approvals add a maker-checker layer to Qualy. You define policies that gate money-moving actions; when an action matches a policy, Qualy holds it and opens an approval request that one or more approvers must decide before it proceeds.


How it works

  1. You create a policy — a rule that says "actions of this type, matching these conditions, need approval from these people."
  2. When a matching entity is created (a payout, a transaction, …), Qualy holds it and opens a request in pending.
  3. Approvers approve or reject the request.
  4. On approval the held action resumes; on rejection it's stopped.

Policies can gate these entity types: transaction, payout, payment-split, authorization, and refund-intent.


Create a policy

POST /v1/approvals/policies/create. At minimum a policy needs a name, an entityType, a strategy, and (for every strategy except auto-approve) a list of approvers. conditions narrow which entities the policy applies to — omit them to match all entities of that type.

curl -X POST https://api.qualyhq.com/v1/approvals/policies/create \
  -H 'Authorization: ApiKey your-api-key-here' \
  -H 'X-TENANT-ID: your-tenant-id-here' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Large payouts need sign-off",
    "entityType": "payout",
    "strategy": "any-of",
    "approvers": ["6606ab7bfb9085579f1b5769"],
    "conditions": {
      "amountMin": 500000,
      "currencyMode": "any"
    }
  }'

Policy fields

FieldDescription
nameRequired. Human-readable label (max 200 chars).
entityTypeRequired. What to gate: transaction, payout, payment-split, authorization, or refund-intent.
strategyRequired. How approval is reached — see Strategies.
approversRequired unless strategy is auto-approve. User ids who can decide (1–100).
conditionsOptional. Narrows which entities match — see Conditions. Omit to match all entities of the type.
priorityOptional. When several policies match, higher priority wins.
enabledOptional. Set false to keep a policy without enforcing it.
partnershipOptional. Scope the policy to one partnership.

Strategies

StrategyHow it resolves
any-ofApproved as soon as one approver approves.
all-ofEvery listed approver must approve.
sequentialApprovers act in listed order, one after another.
round-robinThe request is assigned to approvers in rotation.
auto-approveApproved automatically — no approvers, no waiting. Use to explicitly exempt a matching set from approval.

Conditions

All conditions are optional and combine with AND. An empty or missing condition matches everything.

ConditionMatches on
amountMin / amountMaxAmount range, in cents (amountMax must be ≥ amountMin).
currencyModesame-currency, fx, or any — whether the entity involves a currency conversion.
sourceCurrencies / targetCurrenciesSpecific currencies (e.g. ["AUD","USD"]).
methodsPayment methods to gate (e.g. bank-transfer methods).
actionsEntity actions to gate.
recipientTypesWho's on the receiving end: supplier, partnership, or self.
primaryTeamsTeam ids the entity belongs to.

Manage policies

ActionEndpoint
ListGET /v1/approvals/policies (returned under data.policies)
RetrieveGET /v1/approvals/policies/{policyId}
UpdatePOST /v1/approvals/policies/{policyId}/update
DeletePOST /v1/approvals/policies/{policyId}/delete

Approval requests

When a gated entity is created, Qualy opens a request. List them with GET /v1/approvals/requests (returned under data.requests) or fetch one with GET /v1/approvals/requests/{requestId}.

Request statuses

StatusMeaning
pendingAwaiting a decision. The underlying action is held.
approvedApproved — the held action resumes.
rejectedRejected — the held action is stopped.
canceledThe request was withdrawn (e.g. the underlying entity was canceled).

To check whether a specific entity is waiting on approval, use its summary: GET /v1/approvals/summary/{entityType}/{entityId}.


Approve or reject a request

An approver acts on a pending request. Both endpoints accept an optional comment:

# Approve
curl -X POST https://api.qualyhq.com/v1/approvals/requests/{requestId}/approve \
  -H 'Authorization: ApiKey your-api-key-here' \
  -H 'X-TENANT-ID: your-tenant-id-here' \
  -H 'Content-Type: application/json' \
  -d '{ "comment": "Verified against the signed invoice." }'

# Reject
curl -X POST https://api.qualyhq.com/v1/approvals/requests/{requestId}/reject \
  -H 'Authorization: ApiKey your-api-key-here' \
  -H 'X-TENANT-ID: your-tenant-id-here' \
  -H 'Content-Type: application/json' \
  -d '{ "comment": "Amount does not match the order." }'

With an all-of or sequential policy the request stays pending until the required approvers have all approved; with any-of the first approval resolves it. A single rejection rejects the request under every strategy.


Next steps

Previous
Dunning (chasing payments)