Rules that apply to every step
- Do the steps in order. Do not skip one.
- At each step, compare what you received with Expect. If it differs, follow If not and stop.
- Never invent a value. Every input in the next section comes from the operator or from a response.
- Never send an action to Parmana and then perform it yourself. Parmana releases approved actions to the connector. You only act on the answer.
- Treat anything not explicitly approved as not authorized.
Inputs you need before you start
Every request below carries this header:
Authorization: Bearer <PARMANA_API_KEY>.
Step 1: check the server is reachable and safe
Do: sendGET {PARMANA_URL}/health, then GET {PARMANA_URL}/ready. Neither needs the header.
Expect:
/healthreturns HTTP200and{"status":"UP"}./readyreturns HTTP200and a body with"status":"READY"and"authDisabled":false.
/healthfails or returns anything else: stop. The server is not reachable. Tell the operator./readyreturns HTTP503("status":"NOT_READY"): stop. Its storage is not reachable. Tell the operator./readyshows"authDisabled":true: stop and do not send any real action. That server accepts requests from anyone. Tell the operator that authentication is switched off.
Step 2: check what your key may do
Do: sendGET {PARMANA_URL}/callers/me with the header.
Expect: HTTP 200 and a body like this:
callerId. Your authority.principalId in step 4 must be one of allowedPrincipalIds, and the safe choice is
callerId. Your ACTION must be in allowedCapabilities, or unrestrictedCapabilities must be true.
If not:
- HTTP
401: the key is missing or wrong. Stop. Ask the operator for a valid key. allowedCapabilitiesis empty andunrestrictedCapabilitiesisfalse: your key may invoke nothing. Stop. Ask the operator to grant the capability.ACTIONis not inallowedCapabilities: stop. Ask the operator to grant it. Do not use a different action.
Step 3: check the policy exists
Do: sendPOST {PARMANA_URL}/policies/validate with the header, Content-Type: application/json, and this body:
200 and {"valid":true,"errors":[]}.
If not: HTTP 404 with "valid":false means the policy does not exist at that name and version. Stop. Ask the
operator for the correct name and version. Do not try other versions.
Step 4: build the Business Transaction
A Business Transaction is one JSON object. Every field below is required unless it says otherwise.The consistency rules
The server rejects a request that breaks rules 1 to 5 with HTTP400 and a message that names the rule. A request that breaks rule 6 is treated as a policy rejection instead: HTTP 403 with code POLICY_DENIED and a message that contains declared signal(s) do not match the executed intent.
metadata.businessTransactionIdequalsbusinessTransactionId.authorization.authorityIdequalsauthority.authorityId.intent.authorizationIdequalsauthorization.authorizationId.policy.name,policy.versionandintent.actionare not empty.- Every
businessTransactionIdis a valid UUID. - Where the policy binds a signal to an intent value (a
boundSignalsentry), the signal must equal that value exactly. For example the refund policy bindssignals.refundAmounttointent.parameters.amount, so both must be the same number.
A complete example
This is a real request shape for thecustomer-refund policy. Replace every UUID with a new one, and the timestamps
with the current time.
true unless you have checked it is true. A policy trusts the signals you send for some
facts, so a false true produces an authorization that should not exist.
Step 5: send it
Do: sendPOST {PARMANA_URL}/execute with the header, Content-Type: application/json, and the object from step 4.
Set a client timeout of at least 30 seconds. The request runs the whole pipeline before it answers, including the
call to the connector. The Paytm connector, for example, is allowed up to 10 seconds by default.
Never retry this request automatically. A retry could repeat a real action. The rules for a timeout are in step 7.
Step 6: read the answer
Apply the first row that matches. Every error body is{"error": "<message>"}, with a code when there is one.
Step 7: confirm and keep the record
After an approval (HTTP200), confirm all of these in the response, then store the whole response:
executions[0].decision.outcomeisAPPROVED.verifications[0].statusisVERIFIED.receipts[0]exists.
businessTransactionId together. They are your proof.
After a 409, a timeout, no response, or EXECUTION_RECORD_INCOMPLETE, find out what the server recorded. Send
GET {PARMANA_URL}/trust-records/{businessTransactionId} with the header.
Rules that are never broken
- Never reuse a
businessTransactionId. - Never resend after
EXECUTION_RECORD_INCOMPLETE, or after a timeout you have not resolved with step 7. - Never treat “I could not reach Parmana” as “allowed”.
- Never work around a
POLICY_DENIEDby changing the request until it passes. A denial is a decision. Tell the operator. - Never send a signal you have not verified.
- Never log the API key.
Where the machine readable sources are
If you use an SDK instead of raw requests, the steps and the rules are the same. The
TypeScript SDK and the Python SDK both offer a helper that builds the Business
Transaction and keeps the identifiers consistent. Neither retries
POST /execute for you.
For a human reading this page, the same material with explanations is in Full integration
overview and the Error catalog.