Manager Settlement
Overview
The Manager API lets an authenticated vault manager (or automation) do three things:
Publish a new settlement instruction for a vault batch window.
Query the status of a batch.
Confirm (or mark failed) a batch once off-chain settlement has happened.
The current reference implementation uses an in-memory store for demo/testing. In production, back this with Redis or a database.
Protocol domain: Vault settlements and confirmations
Primary resource:
batch(identified bybatch_id+batch_nonce)Status lifecycle:
pending→completedorfailed
Base URL & Environments
Production:
https://api.devine-os.com/managerStaging:
https://staging.devine-os.com/managerLocal (default FastAPI):
http://127.0.0.1:8000
Replace hostnames with your actual deployment.
All routes below are relative to the Manager base path.
Authentication
All endpoints require authentication via the Devine OS Manager auth layer.
Scheme: Bearer token (JWT or API token), issued by Devine OS
Header:
Authorization: Bearer <token>Tokens encode user permissions, including which
vault_ids the user can manage (used by/settlement/list-pendingfiltering).
HTTP 401 is returned for missing/invalid credentials. HTTP 403 is returned if authenticated but not authorized for a vault.
Idempotency & Uniqueness
A batch is uniquely addressed by the tuple
(batch_id, batch_nonce).batch_idis a unique, ISO-like timestamp key you generate for the window (e.g.,20250608T1410Z).batch_noncemust match on confirm; a mismatch yields HTTP 400.
Units & Encoding
Monetary amounts are passed as strings to avoid JSON number pitfalls.
µUSDC = millionths of a USDC.
Example:
1.23 USDC→1230000µUSDC
nav_at_settlementandsupply_at_settlementare strings of integer-encoded values aligned to your on-chain scaling (e.g., NAV quoted in µUSDC-per-share units and raw share supply).
Status Model
status∈{ "pending", "completed", "failed" }New instructions start as
pending./settlement/confirmsetscompletedorfailed.
Data Models
SettlementInstruction (request body)
SettlementInstruction (request body)batch_id
string
✓
ISO-formatted unique batch key, e.g. 20250608T1410Z.
batch_nonce
integer
✓
Unique nonce for this batch window.
vault_id
string
✓
Vault identifier the batch belongs to.
total_usdc_required
string
Total USDC needed for this batch, in µUSDC.
total_shares
string
Total shares in raw units associated with this batch.
deadline
string (ISO 8601)
Deadline to complete the batch.
quote_time
string (ISO 8601)
Timestamp of the NAV quote used.
notes
string
Freeform context for operators.
Server stores (among other fields):
status="pending", usdc_ready="0", tx_hash="0x000…000", and any provided totals.
ManagerConfirmation (request body)
ManagerConfirmation (request body)batch_id
string
✓
Must match an existing instruction.
batch_nonce
integer
✓
Must match the stored batch_nonce.
status
string
✓
"completed" or "failed".
usdc_ready
string
✓
Amount made available in µUSDC.
tx_hash
string (0x…)
✓
On-chain settlement transaction hash.
nav_at_settlement
string
✓
Positive integer (validated). NAV scale must align with your system.
supply_at_settlement
string
✓
Share supply at settlement, raw units.
Validation:
nav_at_settlementmust parse to a positive integer; otherwise HTTP 400.batch_noncemismatch → HTTP 400.Unknown
batch_id→ HTTP 404.
Endpoints
1) Record a new settlement instruction
POST /settlement/instruction
Creates or overwrites the server record for the batch (idempotent by batch_id).
Request
POST /settlement/instruction
Authorization: Bearer <token>
Content-Type: application/json{
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"vault_id": "vault-main-001",
"total_usdc_required": "125000000",
"total_shares": "987654321",
"deadline": "2025-06-08T14:20:00Z",
"quote_time": "2025-06-08T14:10:00Z",
"notes": "USDC returning from HL withdrawals; batch 42"
}Response 200
{
"status": "instruction accepted",
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"manager": "alice.manager"
}Side-effects (server state)
{
"batch_nonce": 42,
"vault_id": "vault-main-001",
"status": "pending",
"usdc_ready": "0",
"tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"confirmed_by": null,
"total_usdc_required": "125000000",
"total_shares": "987654321"
}2) Retrieve settlement status for a batch
GET /settlement/status
Returns the known state for batch_id. If unknown, returns a canonical pending placeholder (so polling clients don’t 404).
Query Params
batch_id(required)
Request
GET /settlement/status?batch_id=20250608T1410Z
Authorization: Bearer <token>Response 200 (not yet created server-side)
{
"batch_id": "20250608T1410Z",
"batch_nonce": null,
"status": "pending",
"usdc_ready": "0",
"tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"confirmed_by": null,
"nav_at_settlement": "0",
"supply_at_settlement": "0",
"total_usdc_required": null,
"total_shares": null
}Response 200 (existing entry)
{
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"vault_id": "vault-main-001",
"status": "pending",
"usdc_ready": "0",
"tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"confirmed_by": null,
"total_usdc_required": "125000000",
"total_shares": "987654321"
}When confirmed, the same shape is returned with
statusupdated and the confirmation fields populated.
3) List all pending settlement instructions (authorized vaults only)
GET /settlement/list-pending
Returns all pending batches where entry.vault_id ∈ user.vaults.
Request
GET /settlement/list-pending
Authorization: Bearer <token>Response 200
[
{
"batch_nonce": 42,
"vault_id": "vault-main-001",
"status": "pending",
"usdc_ready": "0",
"tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"confirmed_by": null,
"total_usdc_required": "125000000",
"total_shares": "987654321",
"batch_id": "20250608T1410Z"
}
]4) Manager confirms batch settlement
POST /settlement/confirm
Marks a batch as completed or failed and persists settlement metrics.
Request
POST /settlement/confirm
Authorization: Bearer <token>
Content-Type: application/json{
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"status": "completed",
"usdc_ready": "124500000",
"tx_hash": "0x8b5d5f2f2e6e6f0a0b0c0d0e0f1a2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c",
"nav_at_settlement": "123456789",
"supply_at_settlement": "9876543210"
}Response 200
{
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"batch_nonce": 42,
"vault_id": "vault-main-001",
"status": "completed",
"usdc_ready": "124500000",
"tx_hash": "0x8b5d5f2f2e6e6f0a0b0c0d0e0f1a2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c",
"confirmed_by": "alice.manager",
"nav_at_settlement": "123456789",
"supply_at_settlement": "9876543210",
"total_usdc_required": "125000000",
"total_shares": "987654321"
}Errors
404 Batch ID not found→ whenbatch_idis unknown400 batch_nonce mismatch→ whenbatch_noncedoesn’t match stored value400 Invalid nav_at_settlement→ non-integer or non-positive NAV
Request Examples
cURL
curl -X POST "https://api.devine-os.com/manager/settlement/instruction" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"vault_id": "vault-main-001",
"total_usdc_required": "125000000",
"total_shares": "987654321",
"deadline": "2025-06-08T14:20:00Z",
"quote_time": "2025-06-08T14:10:00Z",
"notes": "Return flow"
}'curl "https://api.devine-os.com/manager/settlement/status?batch_id=20250608T1410Z" \
-H "Authorization: Bearer $TOKEN"curl -X POST "https://api.devine-os.com/manager/settlement/confirm" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"status": "completed",
"usdc_ready": "124500000",
"tx_hash": "0x8b5d5f2f2e6e6f0a0b0c0d0e0f1a2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c",
"nav_at_settlement": "123456789",
"supply_at_settlement": "9876543210"
}'JavaScript (fetch)
const base = "https://api.devine-os.com/manager";
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
await fetch(`${base}/settlement/instruction`, {
method: "POST",
headers,
body: JSON.stringify({
batch_id: "20250608T1410Z",
batch_nonce: 42,
vault_id: "vault-main-001",
total_usdc_required: "125000000",
total_shares: "987654321"
})
});
const status = await fetch(`${base}/settlement/status?batch_id=20250608T1410Z`, { headers }).then(r => r.json());
await fetch(`${base}/settlement/confirm`, {
method: "POST",
headers,
body: JSON.stringify({
batch_id: "20250608T1410Z",
batch_nonce: 42,
status: "completed",
usdc_ready: "124500000",
tx_hash: "0x8b5d...a1b2c",
nav_at_settlement: "123456789",
supply_at_settlement: "9876543210"
})
});Python (requests)
import requests, os
base = "https://api.devine-os.com/manager"
headers = {"Authorization": f"Bearer {os.environ['TOKEN']}", "Content-Type": "application/json"}
requests.post(f"{base}/settlement/instruction", headers=headers, json={
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"vault_id": "vault-main-001"
})
status = requests.get(f"{base}/settlement/status", headers=headers, params={"batch_id": "20250608T1410Z"}).json()
requests.post(f"{base}/settlement/confirm", headers=headers, json={
"batch_id": "20250608T1410Z",
"batch_nonce": 42,
"status": "completed",
"usdc_ready": "124500000",
"tx_hash": "0x8b5d...a1b2c",
"nav_at_settlement": "123456789",
"supply_at_settlement": "9876543210"
})Error Handling
Errors use the standard FastAPI shape:
{ "detail": "Message" }Common cases:
400
batch_nonce mismatch
Confirm nonce doesn’t match stored nonce.
400
Invalid nav_at_settlement
NAV not a positive integer or not parseable.
401
Not authenticated
Token missing/invalid.
403
Not authorized
Token valid but not allowed for the vault.
404
Batch ID not found
Confirm called before instruction exists.
429
Rate limit exceeded
If your deployment enforces limits.
Rate Limiting
If enabled in your deployment, rate limits are enforced per token. Contact Devine OS for higher limits.
Operational Notes & Best Practices
Persistence: Replace the demo in-memory store with Redis or a durable DB in production.
Batch keys: Keep
batch_idhuman/time readable (e.g.,YYYYMMDDThhmmZ) and monotonic per vault when possible.Polling: Use
/settlement/statusto poll. Unknown batches return a pending placeholder instead of 404 to simplify clients.Validation: Ensure your automation passes the post-fee, exact µUSDC amounts that will be settled on-chain.
Observability: Log confirmations with
batch_id,tx_hash,nav_at_settlement,supply_at_settlement.
Sequence (Typical Flow)
Manager/Worker → POST /settlement/instruction (create batch; status=pending)
Client → GET /settlement/status?batch_id=… (poll until ready)
Manager/Worker → POST /settlement/confirm (status=completed|failed, attach tx_hash, NAV, supply)
Client → GET /settlement/status?batch_id=… (reads final state)Appendix: Canonical “unknown batch” status payload
When a client polls a batch_id that isn’t yet recorded, the API returns:
{
"batch_id": "<queried id>",
"batch_nonce": null,
"status": "pending",
"usdc_ready": "0",
"tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"confirmed_by": null,
"nav_at_settlement": "0",
"supply_at_settlement": "0",
"total_usdc_required": null,
"total_shares": null
}Implementation Footnotes (for maintainers)
The reference API code imports
get_current_userand maintains_confirm_db: Dict[str, Dict[str, Any]]as the backing store.The server validates
nav_at_settlementas an integer > 0 and enforcesbatch_nonceequality on confirm./settlement/list-pendingfilters byvault_id in user["vaults"].Ensure you expose only one
GET /settlement/statusand oneGET /settlement/list-pendingin your final code (the sample shows duplicates—remove the extra definitions).
Last updated