1. Health & Status
Returns "ok". Use for load balancer health checks.
{ "status": "ok", "message": "Server is running" }
2. Blockchain
Returns the most recently written blockchain block.
Returns the block at numeric index id.
Returns the block whose document DID matches :did.
Returns the last 10 blocks in reverse order.
Validates the integrity (hash chain) of block at index :id.
Response:
{ "valid": true, "blockId": 42, "message": "..." }
Validates a challenge-response proof that a given persona authored a block.
Body:
{
"personaDID": "did:451:...",
"blockHash": "abc123...",
"challengeSignature": "<base64>"
}
Validates that a document block's hash and S3 ETag still match.
Body:
{
"documentDID": "did:451:...",
"documentHash": "<sha256 hex>"
}
Full-text search across the blockchain index via Meilisearch.
Query params:
q(required) — search stringlimit(optional, default 20)
Response:
{
"hits": [{ "documentID": "...", "relevance": 0.9, "note": "Use /api/blockchain/byid/... to fetch" }],
"query": "...",
"processingTimeMs": 3.2,
"limit": 20,
"estimatedTotalHits": 5
}
3. Consensus & Checkpoints
Returns the current consensus configuration: authorized signers, threshold, etc.
Returns the most recently finalized block (one that has met the signature threshold).
Creates a checkpoint summarizing the chain up to a given block.
Body:
{ "upToBlock": 500 }
upToBlock is optional; omit to checkpoint through the latest block.
Compares two checkpoints to detect chain divergence.
Body:
{
"ourCheckpoint": { ... },
"theirCheckpoint": { ... }
}
Response:
{ "diverged": false, "proof": null, "message": "Checkpoints match — no divergence detected" }
4. University / Multi-Signer Onboarding
Creates the genesis node record for a new university participant.
Body:
{
"universityDID": "did:451:uni-...",
"universityName": "Example University",
"publicKey": "<base64 P-256 public key>"
}
Authorizes an additional university as a block signer. Optionally raises the signature threshold.
Body:
{
"universityDID": "did:451:uni-...",
"universityName": "Second University",
"publicKey": "<base64>",
"newSignatureThreshold": 2
}
Response:
{ "success": true, "message": "...", "totalSigners": 2, "signatureThreshold": 2 }
Adds a signature to a block. Once the threshold is met the block is finalized.
Body:
{
"signature": "<base64>",
"publicKey": "<base64>",
"signerDID": "did:451:...",
"signerName": "Optional Name"
}
Response:
{ "signed": true, "finalized": false, "blockHash": "...", "message": "Block signed, awaiting more signatures" }
5. Documents
Submits a document for storage. Uploads to all active S3 providers and writes a blockchain block.
| Header | Required | Description |
|---|---|---|
Content-Type | yes | MIME type of document (e.g. application/pdf) |
X-Submitter-DID or X-Uploader-DID | no | DID of the submitter |
X-Author-DID | no | DID of the author (defaults to submitter) |
Body: Raw document bytes.
Response:
{
"success": true,
"documentId": "doc-<uuid>",
"taskId": "<uuid>",
"message": "Document processed successfully",
"size": 204800,
"filesUploaded": 3,
"blockchainEntries": 1
}
Use taskId with the Progress Tracking endpoints to stream upload progress.
Appends an additional asset (image, attachment) to an existing document package.
| Header | Required | Description |
|---|---|---|
X-Submitter-DID or X-Uploader-DID | yes | DID of the uploader |
X-Asset-Path or X-File-Name | yes | Filename / S3 sub-key for this asset |
Body: Raw asset bytes.
Response:
{
"success": true,
"documentId": "doc-<uuid>",
"assetKey": "privatedocuments/<uuid>/assets/photo.jpg",
"size": 98304,
"providers": ["Backblaze", "Wasabi"],
"providerURLs": ["https://...", "https://..."]
}
Uploads or overwrites the content blob for a draft document.
Body: Raw document bytes (e.g. JSON from a rich-text editor).
Response:
{
"success": true,
"draftId": "doc-<uuid>",
"documentId": "<uuid>",
"s3Key": "privatedocuments/<uuid>/document.json",
"size": 4096,
"providers": ["Backblaze", "Wasabi"]
}
Returns whether metadata and/or content blobs exist for a draft.
Response:
{
"draftId": "doc-<uuid>",
"documentId": "<uuid>",
"hasMetadata": true,
"hasContent": false,
"metadata": { ... }
}
Records a cryptographic signature against a document's S3 head pointer.
Body:
{
"signerDID": "did:451:...",
"signerPublicKey": "<base64>",
"signature": "<base64>",
"role": "author"
}
Response:
{
"success": true,
"documentId": "...",
"signerDID": "did:451:...",
"role": "author",
"entryID": "sign_<uuid>",
"message": "Signature recorded and head updated"
}
6. Signatures — Document Workflow
The preferred signing flow uses a challenge to prove key possession before accepting a document signature.
Generates a signing challenge for an authorized signer.
Body:
{ "signerDID": "did:451:..." }
Response:
{
"challengeId": "<uuid>",
"challengeText": "<random string>",
"expiresAt": "2026-04-06T12:00:00Z",
"message": "Sign the challengeText … then submit to /api/documents/:id/sign-with-challenge"
}
Submits both a challenge signature and a document signature. Sends APNS notifications on completion.
Body:
{
"challengeId": "<uuid>",
"signerDID": "did:451:...",
"challengeSignature": "<base64>",
"documentSignature": "<base64>"
}
Response:
{
"success": true,
"documentId": "...",
"signaturesCollected": 1,
"signaturesRequired": 2,
"isComplete": false
}
Accepts a document signature without a challenge. Prefer sign-with-challenge.
Body:
{ "signerDID": "did:451:...", "signature": "<base64>" }
Returns current signature status for a document.
Response:
{
"documentId": "...",
"documentHash": "<sha256>",
"collectedSignatures": 1,
"requiredSignatures": 2,
"signatures": [
{ "signer": "did:451:...", "status": "verified", "signedAt": "..." }
]
}
Returns the head pointer metadata for a document (latest event type, pointers to prior entries).
Returns documents that need signatures from any of the listed personas.
Query params:
personaDIDs— comma-separated list of DIDssignerDID— (legacy) single DIDlimit(default 50)
Same as above but via POST body (useful for large DID lists).
Body:
{ "personaDIDs": ["did:451:...", "did:451:..."], "limit": 50 }
Returns pending-signature count and document list for a single signer.
Single-persona convenience shortcut for the multi-persona endpoint.
7. Personas
Personas are DID-backed identities aligned with the AT Protocol.
Step 1 of persona creation. Validates and canonicalizes the proposed persona data, returns a canonicalDID and signing payload.
Body: PersonaCanonicalizeRequest
Step 2 of persona creation. Submits the signed canonical payload to persist the persona to S3 and write a blockchain block.
Body: PersonaFinalizeRequest
Returns the PersonaProfile for the given DID.
Performs a verified soft update (name, address, background-validated flag). Requires the private key.
Body:
{
"updatedName": "Alice Smith",
"updatedAddress": "123 Main St",
"updatedBackgroundValidated": true,
"privateKeyBase64": "<raw P-256 key>"
}
Partial update via PersonaUpdateRequest. Ownership is proven via signature in the payload.
Decommissions a persona. The DID remains on the chain but is marked inactive.
Body (optional):
{ "reason": "Account closed" }
Resolves a handle or short ID to a full PersonaProfile.
Full-text or filter-based persona search via Meilisearch (personasIndex).
Re-indexes the persona in Meilisearch. Returns 200 OK.
Checks whether a persona is the declared author or participant of a specific document block.
Verifies that the caller controls the private key for a persona by validating a challenge response.
Body: ChallengeResponsePayload
Returns background-check status for a persona.
Initiates a background-check order via the configured VerifiedCredentials API.
Polls background-check order status.
Use the two-step canonicalize → finalize flow instead.
8. Device Registration
Registers an APNS device token for a persona (global shortcut).
Body:
{ "did": "did:451:...", "deviceToken": "<apns hex token>" }
Registers a device token scoped to a specific persona (preferred).
Body:
{ "did": "did:451:...", "deviceToken": "<apns hex token>" }
9. Proposals
Proposals represent an invitation to join a document workflow (e.g. as a co-signer).
Creates a proposal from one persona to another.
Returns proposal details.
Verifies the cryptographic proof attached to a proposal.
Accepts a proposal, enrolling the recipient as a participant.
10. Authentication — Sign in With 451
Two-party challenge/response flow. Can be used for identity proof or access attestation.
Creates an authentication challenge. Returns a deep link usable by the Signator app.
Body:
{
"relyingPartyDID": "did:451:...",
"purpose": "login",
"mode": "identity_proof",
"callbackURL": "https://yourapp.example/auth/callback"
}
mode values: identity_proof (default) | access_attestation
Response:
{
"challengeId": "...",
"challengeText": "...",
"relyingPartyDID": "did:451:...",
"purpose": "login",
"mode": "identity_proof",
"expiresAt": "2026-04-06T12:05:00Z",
"deepLinkURL": "signator://auth?challenge=..."
}
Fetches a challenge (used by the Signator app to display details before signing).
Submits a signed response to a challenge. If mode is access_attestation a blockchain block is written. Issues a JWT token.
Body:
{
"challengeId": "...",
"personaDID": "did:451:...",
"challengeSignature": "<base64>"
}
Response:
{
"token": "<jwt>",
"personaDID": "did:451:...",
"mode": "identity_proof",
"blockRef": null,
"expiresAt": "2026-04-06T13:00:00Z"
}
Polls for whether a challenge has been responded to and a token issued.
11. Credential Verification
Integration with the VerifiedCredentials background-check API. Configured via VC_ENVIRONMENT, VC_API_USERNAME, VC_API_PASSWORD environment variables.
Submits a new background-check order for a persona.
Body: CredentialCheckRequest
Returns status of an existing background-check order.
Returns all credential assertions on-chain for a DID.
12. Data Repositories & Instruments
Data repositories allow institutions to ingest instrument readings into the 451 blockchain. All ingest calls write an immutable block.
Repository Management
Registers a new data repository. Requires an institutional persona with a DNS-validated domain.
Body:
{
"ownerDID": "did:451:...",
"shortId": "university.edu/admin",
"name": "Weather Station Network",
"description": "Campus weather instruments",
"publicKey": "<base64 P-256>",
"signature": "<base64 — sign(ownerDID:name)>",
"s3Endpoints": ["s3://my-bucket"],
"cacheServerURL": "https://cache.university.edu"
}
Response:
{
"repositoryId": "repo_<uuid>",
"apiKey": "<raw key — shown once>",
"blockIndex": 42,
"blockHash": "...",
"createdAt": "..."
}
Returns repository registration details and the corresponding blockchain block reference.
Rotates the repository API key.
Auth: X-Owner-DID + X-Signature (sign ownerDID:repositoryId:rotate)
Body:
{
"ownerDID": "did:451:...",
"shortId": "university.edu/admin",
"publicKey": "<base64>",
"signature": "<base64>"
}
Response:
{ "repositoryId": "...", "apiKey": "<new raw key>", "rotatedAt": "..." }
Ingest — Streaming Pattern
Use open/close for continuous instruments (weather sensors, body cameras).
Opens a data stream session.
Headers:
X-Repository-API-Key: raw API keyX-Repository-ID: repository ID
Body:
{
"repositoryId": "repo_<uuid>",
"instrumentType": "weather_station",
"metadata": { "location": "Building A rooftop" }
}
Response:
{ "streamId": "stream_<uuid>", "openedAt": "..." }
Closes a stream session and writes a blockchain block.
Headers: Same as open.
Body:
{
"repositoryId": "repo_<uuid>",
"streamId": "stream_<uuid>",
"s3Bucket": "my-bucket",
"s3Key": "readings/2026-04-06/weather.json",
"s3ETag": "\"abc123\"",
"s3ProviderURL": "https://s3.us-east-1.amazonaws.com",
"dataHash": "<sha256 hex>",
"dataSizeBytes": 4096,
"metadata": { "temperatureUnit": "celsius" }
}
Response:
{ "streamId": "...", "blockIndex": 43, "blockHash": "...", "recordedAt": "..." }
Ingest — Single-Shot Pattern
Single-call ingest for instruments that don't hold a session open.
Headers: Same as streaming.
Body: Same fields as ingest/close plus optional capturedAt timestamp. No streamId required (generated server-side).
Instrument Registration (Conformance Spec)
Registers a conformant instrument. The instrument's P-256 key pair must be generated on the instrument; only the public key is sent here. The private key never leaves the instrument.
Body:
{
"repositoryId": "repo_<uuid>",
"ownerDID": "did:451:...",
"shortId": "university.edu/admin",
"ownerPublicKey": "<base64>",
"ownerSignature": "<base64 — sign(repositoryId:instrumentPublicKey)>",
"instrumentPublicKey": "<base64 P-256 — generated on instrument>",
"instrumentType": "weather_station",
"name": "Rooftop Sensor 1"
}
Response:
{
"instrumentDID": "did:451:instrument:<uuid>",
"repositoryId": "...",
"specVersion": "0.1",
"signingInputFormat": "spec:repositoryId:instrumentDID:s3Bucket:s3Key:s3ETag:dataHash:openedAt:closedAt",
"registeredAt": "..."
}
Lists all registered instruments for a repository.
Conformant instrument path. The instrument signs the block locally before submitting. S451 validates and commits.
Header: X-Instrument-DID: did:451:instrument:<uuid>
Body (ConformantIngestSubmission):
{
"payload": {
"spec": "0.1",
"instrumentDID": "did:451:instrument:...",
"repositoryId": "repo_<uuid>",
"s3Bucket": "my-bucket",
"s3Key": "readings/2026-04-06/data.json",
"s3ETag": "\"abc123\"",
"s3ProviderURL": "https://...",
"dataHash": "<sha256>",
"dataSizeBytes": 2048,
"openedAt": "2026-04-06T10:00:00Z",
"closedAt": "2026-04-06T10:01:00Z",
"instrumentType": "weather_station",
"metadata": {}
},
"publicKey": "<base64 — must match registered key>",
"signature": "<base64 — ECDSA P-256 over canonical input>"
}
Canonical signing input format:
spec:repositoryId:instrumentDID:s3Bucket:s3Key:s3ETag:dataHash:openedAt:closedAt
Fields are joined with : in that exact order and signed as UTF-8 bytes.
Response:
{
"instrumentDID": "did:451:instrument:...",
"repositoryId": "...",
"blockIndex": 44,
"blockHash": "...",
"verifiedInput": "0.1:repo_...:did:451:instrument:...:...",
"recordedAt": "..."
}
13. Progress Tracking (SSE)
Long-running operations (document submit, S3 uploads) report progress via Server-Sent Events.
Opens an SSE stream for the given task. The client receives ProgressStep events followed by a CompletionEvent or ErrorEvent.
Snapshot of current progress without streaming.
Response:
{
"taskId": "...",
"steps": [
{ "step": "extracting_metadata", "message": "Analyzing document", "progress": 0.1, "documentId": "..." }
],
"completion": null,
"error": null,
"isComplete": false
}
Creates a synthetic test task and streams fabricated steps. Useful for client SSE integration testing.
Body (optional): { "duration": 5 } (seconds, default 5)
Response: { "taskId": "..." }
14. Signed URLs
Generates a pre-signed S3 URL for a file.
Body:
{
"fileName": "privatedocuments/<uuid>/document.pdf",
"operation": "GET",
"expiration": 3600
}
Response:
{ "signedURL": "https://..." }
15. Timestamp
Submits a timestamp proof request, writing a timestampRecord block to the chain.
Body: TimestampRequestFromWallet
16. GPS Location
Submits a GPS location claim, writing a gpsLocationRecord block to the chain.
Body: GPSLocationClaimRequest
17. Notifications
Sends a notification and records it to the notification blockchain.
Body: NotificationBlockPayload
18. Search & Debug
Checks whether the Meilisearch service is reachable.
Verifies that a specific document is searchable in a given index. Accepts optional ?originalDID= query parameter.
Returns the status of a Meilisearch background task by its numeric UID.
Lists all configured Meilisearch indices and their settings.
Returns up to limit document IDs from a specific index. Does not return full documents — fetch those from S3 using the ID.
Searches all indices for a document ID and reports which indices contain it.
Authentication Model
Most endpoints authenticate requests via signature verification rather than bearer tokens:
| Mechanism | Used by |
|---|---|
| P-256 ECDSA signature over a known message | Repository registration, key rotation, instrument registration |
| Challenge/response (identity challenge service) | auth/respond, sign-with-challenge, persona verify-ownership |
API key header (X-Repository-API-Key) | All data ingest endpoints |
JWT (issued by auth/respond) | Consumer applications polling auth/status |
S3 Path Conventions
| Entity | S3 Key Pattern |
|---|---|
| Private document content | privatedocuments/<uuid>/document.json |
| Document metadata | privatedocuments/<uuid>/metadata.json |
| Document head pointer | privatedocuments/<uuid>/head.json |
| Document signature event | privatedocuments/<uuid>/signatures/<entryID>.json |
| Blockchain block | blockchain/entry_<index>.json |
| Blockchain ledger | blockchain/ledger.json |
| Repository record | repositories/<repositoryId>/record.json |
| API key record | repositories/<repositoryId>/apikeys/<keyId>.json |
| Instrument record | repositories/<repositoryId>/instruments/<instrumentId>.json |
| Stream session | repositories/<repositoryId>/streams/<streamId>.json |
Meilisearch Indices
| Index | Content |
|---|---|
blockchainIndex | Blockchain blocks (all types), repository registrations, ingest records |
personasIndex | Public persona profiles |
privatePersonasIndex | Private persona profiles |
fullTextIndex | Full text of public documents |
privateFullTextIndex | Full text of private documents |
metadataIndex | Document metadata |
signatureIndex | Signature records (filterable on status, documentId, signerId) |