Control an account lifecycle
Create an account
Create the account that will be managed by the lifecycle workflow.
Submit a transition
An operations_maker records a request in PENDING_APPROVAL.
Approve separately
A different operations_checker approves or rejects the request.
Verify state
Read the request and account to confirm the applied transition.
Role matrix
operations_makerCreate accountsSubmit lifecycle requestsRead own requests
operations_checkerReview pending requestsApprove or reject requestsRead all requests
Submit a freeze request
curl -sS -X POST https://ledger.mavula.dev/api/accounts/ACCOUNT_ID/status-transitions \ -H "Authorization: Bearer $MAKER_TOKEN" \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: 7f2b1e3a-4c2d-4d63-9b1a-2f7b6c0d5e91' \ -H 'X-Correlation-ID: 0f3c8d7a-6e1b-4a98-8d7f-9c1a2b3d4e5f' \ -d '{"transition":"FREEZE","reason":"Suspected fraud"}'const response = await fetch(`${ledgerUrl}/api/accounts/${accountId}/status-transitions`, { method: 'POST', headers: { authorization: `Bearer ${makerToken}`, 'content-type': 'application/json', 'idempotency-key': crypto.randomUUID(), 'x-correlation-id': crypto.randomUUID(), }, body: JSON.stringify({ transition: 'FREEZE', reason: 'Suspected fraud' }),});response = requests.post( f"{ledger_url}/api/accounts/{account_id}/status-transitions", headers={"Authorization": f"Bearer {maker_token}", "Idempotency-Key": str(uuid.uuid4()), "X-Correlation-ID": str(uuid.uuid4())}, json={"transition": "FREEZE", "reason": "Suspected fraud"}, timeout=10,)response.raise_for_status()HttpRequest request = HttpRequest.newBuilder(URI.create(ledgerUrl + "/api/accounts/" + accountId + "/status-transitions")) .header("Authorization", "Bearer " + makerToken) .header("Content-Type", "application/json") .header("Idempotency-Key", UUID.randomUUID().toString()) .header("X-Correlation-ID", UUID.randomUUID().toString()) .POST(HttpRequest.BodyPublishers.ofString("{\"transition\":\"FREEZE\",\"reason\":\"Suspected fraud\"}")) .build();body := strings.NewReader(`{"transition":"FREEZE","reason":"Suspected fraud"}`)req, _ := http.NewRequest(http.MethodPost, ledgerURL+"/api/accounts/"+accountID+"/status-transitions", body)req.Header.Set("Authorization", "Bearer "+makerToken)req.Header.Set("Content-Type", "application/json")req.Header.Set("Idempotency-Key", uuid.NewString())req.Header.Set("X-Correlation-ID", uuid.NewString())Approve as a different operator
Section titled “Approve as a different operator”curl -sS -X POST https://ledger.mavula.dev/api/account-lifecycle-requests/REQUEST_ID/approve \ -H "Authorization: Bearer $CHECKER_TOKEN" \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: a65e192f-aa5f-4dc9-a111-baf6136595a0' \ -d '{"reason":"Evidence reviewed"}'Failure handling
Section titled “Failure handling”| HTTP status | Meaning | Resolution |
|---|---|---|
400 |
Invalid transition or malformed body | Validate transition and reason |
403 |
Missing permission or self-approval | Use a distinct checker with finance.approve |
404 |
Account or request not found in the tenant | Verify identifiers and tenant context |
409 |
Idempotency or state conflict | Reuse the key only for an identical retry; refresh current state |