Manager Settlement


Overview

The Manager API lets an authenticated vault manager (or automation) do three things:

  1. Publish a new settlement instruction for a vault batch window.

  2. Query the status of a batch.

  3. 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 by batch_id + batch_nonce)

  • Status lifecycle: pendingcompleted or failed


Base URL & Environments

  • Production: https://api.devine-os.com/manager

  • Staging: https://staging.devine-os.com/manager

  • Local (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-pending filtering).

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_id is a unique, ISO-like timestamp key you generate for the window (e.g., 20250608T1410Z).

  • batch_nonce must 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 USDC1230000 µUSDC

  • nav_at_settlement and supply_at_settlement are 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/confirm sets completed or failed.


Data Models

SettlementInstruction (request body)

Field
Type
Required
Description

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)

Field
Type
Required
Rules

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_settlement must parse to a positive integer; otherwise HTTP 400.

  • batch_nonce mismatch → HTTP 400.

  • Unknown batch_idHTTP 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 status updated 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 → when batch_id is unknown

  • 400 batch_nonce mismatch → when batch_nonce doesn’t match stored value

  • 400 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:

HTTP
detail
When

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_id human/time readable (e.g., YYYYMMDDThhmmZ) and monotonic per vault when possible.

  • Polling: Use /settlement/status to 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_user and maintains _confirm_db: Dict[str, Dict[str, Any]] as the backing store.

  • The server validates nav_at_settlement as an integer > 0 and enforces batch_nonce equality on confirm.

  • /settlement/list-pending filters by vault_id in user["vaults"].

  • Ensure you expose only one GET /settlement/status and one GET /settlement/list-pending in your final code (the sample shows duplicates—remove the extra definitions).

Last updated