Appearance
Touli API and Event Contract Blueprint
Purpose
Define request/response contracts and internal event contracts for the deferred architecture modules.
Contract Versioning Strategy
- Public API versioning:
- URI version prefix:
/v1/... - additive changes are backward-compatible
- breaking changes require
/v2
- URI version prefix:
- Event schema versioning:
event_nameevent_versionschema_hash- consumers reject unknown required fields and tolerate unknown optional fields
Cross-Cutting Headers
Required on mutating APIs:
X-Request-IdX-Idempotency-KeyX-Client-Version
API Contracts
1) Submit Deed
POST /v1/deeds
Request:
json
{
"pillar": "social",
"hoursClaimed": 3,
"activityAt": "2026-06-22T08:00:00Z",
"beneficiaryFlag": "others",
"challengeId": "optional-uuid",
"challengeTaskId": "optional-uuid",
"location": { "lat": 6.9271, "lng": 79.8612 },
"evidence": [
{ "type": "photo", "uploadRef": "storage/key/1" },
{ "type": "ngo_qr", "value": "QR_PAYLOAD" }
]
}Response 202 Accepted:
json
{
"submissionId": "uuid",
"status": "pending",
"statusUrl": "/v1/deeds/uuid/status",
"receivedAt": "2026-06-22T08:00:02Z"
}2) Get Verification Status
GET /v1/deeds/{submissionId}/status
Response:
json
{
"submissionId": "uuid",
"status": "processing",
"verificationOutcome": null,
"confidenceScore": null,
"reasonCodes": [],
"updatedAt": "2026-06-22T08:01:00Z"
}Terminal response sample:
json
{
"submissionId": "uuid",
"status": "approved",
"verificationOutcome": "approved",
"verifyLevel": "peer",
"groupSizeBucket": "group",
"beneficiaryFlag": "others",
"reasonCodes": ["PEER_CONFIRMED", "GPS_MATCHED"],
"updatedAt": "2026-06-22T08:01:20Z"
}3) Get Points Ledger
GET /v1/points/ledger?cursor=...&limit=...
Response:
json
{
"items": [
{
"ledgerId": "uuid",
"entryType": "deed_reward",
"pointsDelta": 273,
"createdAt": "2026-06-22T08:02:00Z",
"configSnapshotId": "uuid",
"reasonCodes": ["FORMULA_V4_2"]
}
],
"nextCursor": "opaque-cursor"
}4) Get Anchor Proof (Internal/Operator)
GET /v1/points/ledger/{ledgerId}/anchor-proof
Response:
json
{
"ledgerId": "uuid",
"leafHash": "hex",
"batchId": "uuid",
"merkleRoot": "hex",
"txHash": "0x...",
"chainId": 137,
"proofPath": ["hex1", "hex2"],
"status": "confirmed"
}Internal Event Contracts
Envelope
json
{
"eventName": "VerificationCompleted",
"eventVersion": 1,
"eventId": "uuid",
"occurredAt": "2026-06-22T08:01:20Z",
"traceId": "uuid",
"idempotencyKey": "string",
"payload": {}
}1) DeedSubmitted.v1
Payload:
json
{
"submissionId": "uuid",
"userId": "uuid",
"pillar": "social",
"hoursClaimed": 3,
"beneficiaryFlag": "others",
"activityAt": "2026-06-22T08:00:00Z"
}Producer:
- Submission service
Consumers:
- Verification worker
2) VerificationCompleted.v1
Payload:
json
{
"submissionId": "uuid",
"userId": "uuid",
"verificationOutcome": "approved",
"verifyLevel": "peer",
"groupSizeBucket": "group",
"beneficiaryFlag": "others",
"confidenceScore": 0.83,
"reasonCodes": ["GPS_MATCHED", "PEER_CONFIRMED"],
"decisionVersion": 1
}Producer:
- Verification worker
Consumers:
- Points worker, notifications
3) PointsCredited.v1
Payload:
json
{
"ledgerId": "uuid",
"submissionId": "uuid",
"userId": "uuid",
"entryType": "deed_reward",
"pointsDelta": 273,
"configSnapshotId": "uuid"
}Producer:
- Points worker
Consumers:
- Anchor worker, read model projector, notifications
4) AnchorBatchCreated.v1
Payload:
json
{
"batchId": "uuid",
"leafCount": 231,
"merkleRoot": "hex",
"windowStart": "2026-06-22T00:00:00Z",
"windowEnd": "2026-06-22T23:59:59Z"
}Producer:
- Anchor worker
Consumers:
- Ops monitoring
5) AnchorConfirmed.v1
Payload:
json
{
"batchId": "uuid",
"txHash": "0x...",
"blockNumber": 123456,
"confirmationCount": 20,
"confirmedAt": "2026-06-23T00:07:00Z"
}Producer:
- Anchor worker
Consumers:
- Audit surfaces, reporting
Idempotency Rules
- API mutating operations require
X-Idempotency-Key. - Event processing key:
eventName + eventVersion + payload.primaryId + decisionVersion
- Consumers must store processed key and no-op duplicates.
Compatibility Rules
- Additive optional fields are allowed in same version.
- New required field requires event version bump.
- Deprecated fields retained for at least one full release cycle.