Introduction
The Sparker Verify API provides programmatic access to cryptographic media verification. All endpoints are served over HTTPS from https://verify.sparker.io/api/v1.
The API follows REST conventions with JSON request and response bodies. Multipart form data is used for file uploads.
Base URL: https://verify.sparker.io/api/v1
Authentication
Sparker has two auth modes. Dashboard management uses Google OAuth and JWT bearer tokens. SDK traffic should use dashboard-generated API keys in the Authorization header.
Public verification endpoints can be called without authentication, but those requests are much more heavily rate-limited than requests that include an API key.
OAuth Flow
- Redirect user to
GET /api/v1/auth/google - User authenticates with Google
- Callback redirects to your app with
access_token - Include token in subsequent requests
GET /api/v1/auth/me
Get the current authenticated user.
curl https://verify.sparker.io/api/v1/auth/me \ -H "Authorization: Bearer your-jwt-access-token"
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "Jane Doe",
"avatar_url": "https://lh3.googleusercontent.com/...",
"oauth_provider": "google",
"email_verified": true,
"created_at": "2026-01-15T10:30:00Z"
}POST /api/v1/auth/refresh
Refresh an expired access token using the refresh token cookie.
POST /api/v1/auth/logout
Invalidate the current session and clear refresh token.
API Keys
Create API keys from the dashboard, then initialize the SDK with that key and reuse the client across your app.
POST /api/v1/api-keys
Create a new API key. The full key value is only returned once.
curl -X POST https://verify.sparker.io/api/v1/api-keys \
-H "Authorization: Bearer your-jwt-access-token" \
-H "Content-Type: application/json" \
-d '{"name": "Production SDK"}'{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production SDK",
"key_prefix": "spk_live_abc123",
"key": "spk_live_abc123def456ghi789...",
"is_active": true,
"created_at": "2026-01-15T10:30:00Z",
"last_used_at": null,
"revoked_at": null
}GET /api/v1/api-keys
List active API keys for the authenticated dashboard user.
DELETE /api/v1/api-keys/:id
Revoke an API key. Returns 204 No Content.
Browser Device Registration
Devices must be registered with hardware attestation before they can sign media. Each device generates an ECDSA P-256 key pair via WebAuthn. This production flow is web/browser only today; native iOS and Android SDKs are not supported yet.
POST /api/v1/devices/challenge
Generate a challenge for device registration. The client signs this challenge with the device's private key during WebAuthn registration. Accepts either a Bearer API key or a 3P verification link token in X-Token.
| Parameter | Type | Description |
|---|---|---|
| Authorization | string | Bearer API key or dashboard JWT |
| X-Token | string | 3P verification link token |
{
"challenge": "dGhpcyBpcyBhIGNoYWxsZW5nZQ..."
}POST /api/v1/devices/register
Register a device with attestation proof and public key. Accepts either a Bearer API key or a 3P verification link token in X-Token.
curl -X POST https://verify.sparker.io/api/v1/devices/register \
-H "Authorization: Bearer spk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"platform": "web",
"device_id": "credential-id-base64",
"public_key": {
"algorithm": "ES256",
"x": "base64-x-coordinate",
"y": "base64-y-coordinate"
},
"attestation": {
"format": "webauthn",
"data": "attestation-object-base64",
"challenge_response": "client-data-json-base64"
},
"device_info": {
"name": "Chrome on macOS",
"model": "Chrome",
"os_version": "macOS 15.3"
}
}'{
"id": "550e8400-e29b-41d4-a716-446655440000",
"device_id": "credential-id-base64",
"name": "Chrome on macOS",
"model": "Chrome",
"platform": "web",
"registered_at": "2026-01-15T10:30:00Z"
}GET /api/v1/devices
List all registered devices for the authenticated user.
DELETE /api/v1/devices/:id
Remove a registered device.
Verifications
The verification flow is a two-step process: create a verification request to get a nonce, then submit signed media to complete the verification.
POST /api/v1/verifications
Create a verification request. Returns a nonce that must be signed with the media. The nonce expires after 5 minutes.
| Parameter | Type | Description |
|---|---|---|
| Authorization | string | Bearer API key or dashboard JWT |
| X-Token | string | 3P verification link token |
curl -X POST https://verify.sparker.io/api/v1/verifications \ -H "Authorization: Bearer spk_live_your_api_key"
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"nonce": "random-nonce-base64",
"expires_at": "2026-01-15T10:35:00Z"
}POST /api/v1/verifications/verify
Submit signed media to complete verification. Uses multipart form data. The signature must be Sign(Hash(file + nonce)) using the device's private key.
| Parameter | Type | Description |
|---|---|---|
| id* | UUID | Verification ID from creation step |
| file* | file | Media file (image, video, or audio) |
| device_id* | string | Registered device identifier |
| signature* | string | Base64 signature of Hash(file + nonce) |
| signature_metadata | JSON string | Signing algorithm metadata |
| metadata | JSON string | GPS coordinates and other metadata |
| authenticator_data | string | WebAuthn authenticatorData (base64url) |
| client_data_json | string | WebAuthn clientDataJSON (base64url) |
curl -X POST https://verify.sparker.io/api/v1/verifications/verify \
-H "Authorization: Bearer spk_live_your_api_key" \
-F "id=550e8400-e29b-41d4-a716-446655440000" \
-F "file=@photo.jpg" \
-F "device_id=credential-id-base64" \
-F "signature=signature-base64" \
-F 'metadata={"latitude":37.7749,"longitude":-122.4194}'{
"id": "550e8400-e29b-41d4-a716-446655440000",
"authentic": true,
"signature_hash": "abc123def4567890abc123def4567890abc123def4567890abc123def4567890",
"verified_at": "2026-01-15T10:31:00Z",
"device": {
"name": "Chrome on macOS",
"model": "Chrome",
"platform": "web"
},
"gps": {
"latitude": 37.7749,
"longitude": -122.4194,
"accuracy": 10.0
}
}GET /api/v1/verifications/:id
Get verification details by ID.
3P Verification Links
3P verification links let an account holder delegate capture flows to third parties. Verifications created via these links count against the creator's quota.
POST /api/v1/verifications/external
Create a new 3P verification link. Requires a Pro or Enterprise plan.
| Parameter | Type | Description |
|---|---|---|
| max_uses | integer | Maximum number of uses (null for unlimited) |
| expires_at | ISO 8601 | Expiration timestamp (null for no expiry) |
curl -X POST https://verify.sparker.io/api/v1/verifications/external \
-H "Authorization: Bearer spk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"max_uses": 10, "expires_at": "2026-02-01T00:00:00Z"}'{
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "spk_abc123def456...",
"url": "https://verify.sparker.io/capture?token=spk_abc123def456...",
"max_uses": 10,
"expires_at": "2026-02-01T00:00:00Z",
"created_at": "2026-01-15T10:30:00Z"
}GET /api/v1/verifications/external
List all 3P verification links for the authenticated account.
| Parameter | Type | Description |
|---|---|---|
| include_inactive | boolean | Include revoked/expired tokens (default: false) |
DELETE /api/v1/verifications/external/:id
Revoke an active token. Returns 204 No Content.
GET /api/v1/verifications/external/validate/:token
Validate a 3P verification link token. Public endpoint (no authentication required).
{
"valid": true,
"creator_name": "Jane Doe",
"remaining_uses": 8,
"expires_at": "2026-06-15T10:30:00Z"
}Public Verification
Anyone can verify media authenticity using these public endpoints. The SDK extracts signature hashes locally so files never need to be uploaded for verification. These endpoints can be called without authentication, but adding a Bearer API key unlocks much higher request limits. The backend accepts either the registered device-signature hash or the stored file hash as the public lookup key.
POST /api/v1/verify
Check if a public lookup hash is registered.
curl -X POST https://verify.sparker.io/api/v1/verify \
-H "Content-Type: application/json" \
-d '{"signature_hash": "abc123def4567890abc123def4567890abc123def4567890abc123def4567890"}'{
"authentic": true,
"verification_id": "550e8400-e29b-41d4-a716-446655440000",
"verified_at": "2026-01-15T10:31:00Z"
}GET /api/v1/verify/:id
Get full verification details including GPS, device info, and C2PA manifest for a public or 3P verification record.
POST /api/v1/verify/batch
Verify up to 100 signature hashes in a single request.
curl -X POST https://verify.sparker.io/api/v1/verify/batch \
-H "Content-Type: application/json" \
-d '{"signatures": ["abc123def456...", "7890fedcba12..."]}'Quota
Each user has a monthly verification quota based on their tier. Quotas reset on the 1st of each month at midnight UTC.
| Tier | Monthly Verifications | Devices | Active Tokens |
|---|---|---|---|
| Free | 50 | 2 | 5 |
| Pro | 2,000 | 10 | 50 |
| Enterprise | Unlimited | Unlimited | Unlimited |
GET /api/v1/quota
Get current quota status for the authenticated user.
curl https://verify.sparker.io/api/v1/quota \ -H "Authorization: Bearer your-jwt-access-token"
{
"quota_tier": "free",
"monthly_quota": 50,
"quota_used_this_month": 42,
"quota_reset_at": "2026-02-01T00:00:00Z",
"device_limit": 2,
"device_count": 1,
"token_limit": 5,
"active_token_count": 2
}Rate Limits
Rate limits vary based on authentication status. When rate limited, the API returns 429 Too Many Requests with a Retry-After header.
| User Type | Base Rate | Verification Creation | Signature Verification | |
|---|---|---|---|---|
| Public (no API key) | 10/min | n/a | 3/min | |
| 3P link token | 10/min | 10/min | n/a | |
| API key / dashboard session | 10/min | 10/min | 100/min | 120/min |
Rate limit headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1706356200
Retry-After: 30
Errors
The API uses standard HTTP status codes. Error responses include a detail field with a human-readable description.
| Status | Description |
|---|---|
| 400 | Bad Request - Invalid parameters or malformed request |
| 401 | Unauthorized - Missing or invalid authentication |
| 403 | Forbidden - Insufficient permissions for this resource |
| 404 | Not Found - Resource does not exist |
| 409 | Conflict - Resource already exists (e.g., duplicate device) |
| 422 | Unprocessable Entity - Valid JSON but invalid data |
| 429 | Too Many Requests - Rate limit or quota exceeded |
| 500 | Internal Server Error - Something went wrong on our end |
Error response format
{
"detail": "Verification not found"
}