Appearance
API Reference
Base URLs:
- Production:
https://api.ultimalotto.com - Sandbox:
https://sandbox.ultimalotto.com
Rate Limits
All endpoints are rate-limited:
| Tier | Limit | Identifier |
|---|---|---|
| Public (no auth) | 60 requests/minute | IP address |
| Authenticated (API key or JWT) | 120 requests/minute | API key |
Response headers on every request:
X-RateLimit-Limit— max requests per windowX-RateLimit-Remaining— requests remainingX-RateLimit-Reset— seconds until window resets
Exceeding the limit returns HTTP 429 with { "error": "Rate limit exceeded" }.
Public API (No Auth)
GET /api/public/rounds
List rounds with pagination.
Query: limit (default 50), offset (default 0)
Response:
json
{
"rounds": [
{
"id": 42,
"state": "CLOSED",
"ticketPriceUsdc": 3000000,
"totalTickets": 1500,
"operatorCount": 3,
"escrowedOperatorCount": 3,
"excludedOperatorCount": 0,
"fullManifestHash": "abc123...",
"effectiveManifestHash": "def456...",
"drawSeed": "a1b2c3...",
"winnerIndex": 42,
"winningOperatorId": "op_xxx",
"createdAt": "2025-01-15T12:00:00.000Z",
"scheduledDrawTime": "2025-01-15T14:00:00.000Z"
}
],
"count": 1
}GET /api/public/rounds/:id
Get a single round by ID.
Response: Full round object including operatorIds, escrowedOperatorIds, excludedOperatorIds, previousRoundHash, chainedAnchorHash.
GET /api/public/rounds/:id/events
Get event log for a round.
Response:
json
{
"events": [
{
"type": "ROUND_OPENED",
"roundId": 42,
"timestamp": "2025-01-15T12:00:00.000Z",
"data": {}
}
],
"count": 1
}GET /api/public/operators
List all operators.
Response:
json
{
"operators": [
{
"operatorId": "op_xxx",
"name": "Acme Lottery",
"jurisdiction": "Malta",
"active": true,
"reputationScore": 100,
"totalRoundsParticipated": 10,
"totalRoundsWon": 1,
"registeredAt": "2025-01-01T00:00:00.000Z"
}
],
"count": 1
}GET /api/public/operators/:id
Get a single operator by ID.
GET /api/public/stats
Protocol statistics.
Response:
json
{
"totalRounds": 100,
"closedRounds": 95,
"failedRounds": 2,
"activeRounds": 3,
"totalOperators": 15,
"activeOperators": 12,
"totalTicketsSold": 150000,
"protocolLaunchDate": "2025-01-01T00:00:00.000Z"
}POST /api/public/apply
Submit operator application.
Body:
json
{
"companyName": "Acme Lottery",
"jurisdiction": "Malta",
"gamingLicenseNumber": "MGA/123",
"contactEmail": "ops@acme.com",
"contactName": "Jane Doe",
"ethereumAddress": "0x...",
"website": "https://acme.com",
"description": "Optional description"
}Response:
json
{
"success": true,
"applicationId": "app_xxx",
"applicationToken": "ult_xxx",
"status": "pending",
"message": "Application submitted. Save your application token to check status.",
"warning": "Save this application token — it cannot be retrieved again."
}GET /api/public/application-status/:id
Check application status. Requires X-Application-Token header.
Response (approved):
json
{
"applicationId": "app_xxx",
"companyName": "Acme Lottery",
"status": "approved",
"operatorId": "op_xxx",
"onboarding": {
"step1": "Post your operator bond on-chain...",
"step2": "Install the Operator SDK...",
"sdkDocs": "https://docs.ultimalotto.com/operator-sdk"
}
}Operator API (Auth: X-Ultima-Key or Bearer JWT)
POST /api/operator/manifest
Submit a manifest for a round.
Body:
json
{
"roundId": 42,
"manifestJson": "{\"entries\":[...]}",
"manifestHash": "sha256_hex_string",
"ticketCount": 500
}Response:
json
{
"success": true,
"roundId": 42,
"state": "OPEN",
"sealedCount": 1,
"totalInvited": 3
}POST /api/operator/heartbeat
Record liveness heartbeat.
Response:
json
{
"success": true,
"operatorId": "op_xxx",
"timestamp": "2025-01-15T12:00:00.000Z"
}PUT /api/operator/webhook
Register or update webhook URL.
Body:
json
{
"url": "https://your-domain.com/webhook"
}Response:
json
{
"success": true,
"operatorId": "op_xxx",
"webhookUrl": "https://your-domain.com/webhook"
}GET /api/operator/rounds
Get rounds the operator is invited to.
Response:
json
{
"rounds": [...],
"count": 5
}GET /api/operator/me
Get operator profile.
Response: Operator object (excluding apiKeyHash).
POST /api/operator/test-webhook
Send a test webhook (sandbox). Body: { "eventType": "round.opened" }.
POST /api/operator/test-webhook/all
Send all webhook event types (sandbox).
GET /api/operator/webhook-logs
View recent webhook delivery logs (sandbox).
Auth API
POST /api/auth/operator/login
Exchange API key for JWT pair.
Body:
json
{
"apiKey": "ulk_your_api_key"
}Response:
json
{
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"expiresIn": 900
}POST /api/auth/refresh
Refresh access token. Body: { "refreshToken": "..." }. Old refresh token is revoked.
POST /api/auth/logout
Revoke refresh token. Body: { "refreshToken": "..." }.
Error Codes
See Error Reference for HTTP status codes and error response format.