Skip to main content
This page is written to be followed literally by an AI agent or a person. It contains no options to weigh and no background to interpret. If your situation is not covered, stop and ask the operator. Do not guess.

Rules that apply to every step

  1. Do the steps in order. Do not skip one.
  2. At each step, compare what you received with Expect. If it differs, follow If not and stop.
  3. Never invent a value. Every input in the next section comes from the operator or from a response.
  4. Never send an action to Parmana and then perform it yourself. Parmana releases approved actions to the connector. You only act on the answer.
  5. 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: send GET {PARMANA_URL}/health, then GET {PARMANA_URL}/ready. Neither needs the header. Expect:
  1. /health returns HTTP 200 and {"status":"UP"}.
  2. /ready returns HTTP 200 and a body with "status":"READY" and "authDisabled":false.
If not:
  1. /health fails or returns anything else: stop. The server is not reachable. Tell the operator.
  2. /ready returns HTTP 503 ("status":"NOT_READY"): stop. Its storage is not reachable. Tell the operator.
  3. /ready shows "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: send GET {PARMANA_URL}/callers/me with the header. Expect: HTTP 200 and a body like this:
Keep 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:
  1. HTTP 401: the key is missing or wrong. Stop. Ask the operator for a valid key.
  2. allowedCapabilities is empty and unrestrictedCapabilities is false: your key may invoke nothing. Stop. Ask the operator to grant the capability.
  3. ACTION is not in allowedCapabilities: stop. Ask the operator to grant it. Do not use a different action.

Step 3: check the policy exists

Do: send POST {PARMANA_URL}/policies/validate with the header, Content-Type: application/json, and this body:
Expect: HTTP 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 HTTP 400 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.
  1. metadata.businessTransactionId equals businessTransactionId.
  2. authorization.authorityId equals authority.authorityId.
  3. intent.authorizationId equals authorization.authorizationId.
  4. policy.name, policy.version and intent.action are not empty.
  5. Every businessTransactionId is a valid UUID.
  6. Where the policy binds a signal to an intent value (a boundSignals entry), the signal must equal that value exactly. For example the refund policy binds signals.refundAmount to intent.parameters.amount, so both must be the same number.

A complete example

This is a real request shape for the customer-refund policy. Replace every UUID with a new one, and the timestamps with the current time.
Never set a signal to 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: send POST {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 (HTTP 200), confirm all of these in the response, then store the whole response:
  1. executions[0].decision.outcome is APPROVED.
  2. verifications[0].status is VERIFIED.
  3. receipts[0] exists.
If any is missing, treat the action as not confirmed and tell the operator. Store the response and the 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

  1. Never reuse a businessTransactionId.
  2. Never resend after EXECUTION_RECORD_INCOMPLETE, or after a timeout you have not resolved with step 7.
  3. Never treat “I could not reach Parmana” as “allowed”.
  4. Never work around a POLICY_DENIED by changing the request until it passes. A denial is a decision. Tell the operator.
  5. Never send a signal you have not verified.
  6. 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.