SPATEMO API
SPATEMO is the infrastructure layer for the physical web. Register any real-world object as an EMO — an Executable Media Object — and it becomes detectable, interactive, and royalty-bearing across every camera surface, with no QR code, no app install, and no modification to the object itself.
This reference covers the full API: registering and managing EMOs, querying the detection endpoint, reading the royalty ledger, and receiving event webhooks.
Access is invite-only during the private beta. Submit your use case at the Developer Portal — credentials are issued within 24 hours of approval.
Base URL
https://spatemo.com/v1
Versioning
The API is versioned via the URL path. The current stable version is v1. Breaking changes will be introduced under a new version prefix with at least 90 days notice.
Authentication
All requests must include a bearer token issued to your account. Tokens are prefixed spat_live_ for production and spat_test_ for the sandbox environment.
# Every request requires this header
Authorization: Bearer spat_live_••••••••••••••••
Keep your API key secret. Do not expose it in client-side code, public repositories, or build logs. If a key is compromised, rotate it immediately from the Developer Portal — the old key is invalidated within 60 seconds.
Test vs. Live keys
| Prefix | Environment | Behavior |
|---|---|---|
| spat_test_ | Sandbox | Recognition runs against test EMOs only. No ledger settlements. No charges. |
| spat_live_ | Production | Full detection against registered EMOs. Ledger settlements are real. |
Quickstart
Register your first EMO and make it detectable in three steps.
1. Register an EMO
POST a reference image and your desired CTA configuration. SPATEMO extracts visual fingerprints and returns a unique EMO ID.
curl -X POST https://spatemo.com/v1/emos \
-H "Authorization: Bearer spat_live_..." \
-H "Content-Type: multipart/form-data" \
-F "name=CR7 Jersey" \
-F "image=@cr7_jersey.jpg" \
-F "ctaLabel=Buy Now" \
-F "buyUrl=https://nike.com/cr7"
// Response
{
"id": "emo_1a2b3c4d",
"name": "CR7 Jersey",
"status": "indexing",
"ctaLabel": "Buy Now",
"buyUrl": "https://nike.com/cr7",
"createdAt": "2026-06-26T14:22:00Z"
}
Indexing typically completes in under 30 seconds. Poll GET /v1/emos/{id} until status is "active" before sending detection requests.
2. Send a detection request
From any camera surface — web, native, or wearable — POST a frame or image crop to the detection endpoint. SPATEMO returns the matched EMO and its CTA payload.
curl -X POST https://spatemo.com/v1/detect \
-H "Authorization: Bearer spat_live_..." \
-H "Content-Type: multipart/form-data" \
-F "image=@camera_frame.jpg" \
-F "sessionId=usr_session_xyz"
// Response — match found
{
"matched": true,
"emoId": "emo_1a2b3c4d",
"confidence": 0.97,
"cta": {
"label": "Buy Now",
"url": "https://nike.com/cr7"
},
"detectionId": "det_9z8y7x"
}
3. Handle the CTA in your UI
Use the cta payload to render whatever experience makes sense for your surface — an overlay, a push notification, a native sheet. The detectionId is recorded in the royalty ledger automatically.
EMOs
An EMO — Executable Media Object — is SPATEMO's fundamental unit. It's the binding between a physical object in the real world and a digital address on the internet. Once registered, every instance of that object anywhere in the world shares the same address.
What makes a good EMO candidate
SPATEMO's recognition is visual-fingerprint based, so the object should have consistent, distinctive visual characteristics. Objects that work well:
| Object type | Why it works |
|---|---|
| Consumer product packaging | Consistent print across millions of units. Highly distinctive label art. |
| Sports jerseys & merchandise | Unique number/logo combinations. High-contrast graphics. |
| Event tickets & lanyards | Printed designs with sparse, identifiable patterns. |
| Branded apparel | Logo patches, tags, and print treatments. |
| Trading cards & collectibles | Dense visual information with high per-card uniqueness. |
EMO status lifecycle
| Status | Meaning |
|---|---|
| indexing | Reference image received. Visual fingerprint extraction in progress. |
| active | EMO is live. Detection requests will match against it. |
| paused | Temporarily removed from the detection index. No matches will fire. |
| archived | Permanently removed. Historical detections remain in the ledger. |
Reference image guidelines
Supply the highest-quality reference image available. SPATEMO will normalize it, but better input produces more reliable detections.
- Minimum resolution: 800 × 800px
- Formats accepted: JPEG, PNG, WebP
- Avoid heavy motion blur or extreme perspective distortion
- A clean, front-facing shot of the primary face of the object is ideal
- Multiple reference images per EMO can be supplied for multi-angle coverage
Recognition
When a camera frame is submitted to POST /v1/detect, SPATEMO computes a visual fingerprint from the frame and queries the index for the nearest registered EMO. If the confidence score exceeds the threshold, a match is returned.
Confidence threshold
By default, SPATEMO returns a match only when confidence is ≥ 0.90. You can lower this threshold per-request using the minConfidence parameter, but values below 0.75 will produce a higher false-positive rate.
No-match response
{
"matched": false,
"candidates": []
}
A no-match response means no registered EMO was found above the threshold. The frame is not stored.
Session IDs
Passing a sessionId with detection requests groups multiple detections from the same user session. This is used for deduplication in the royalty ledger — a single user encountering the same EMO repeatedly in one session counts as one billable detection event by default.
Session IDs should be opaque identifiers — do not use PII (names, emails, device IDs) as session values. Use a random UUID generated per session.
Royalty Ledger
Every confirmed detection event that results in a CTA action is recorded in the SPATEMO royalty ledger. Revenue is split automatically among four parties.
Settlement
Ledger balances are settled monthly to registered payout accounts. Minimum payout threshold is $25. Balances below the threshold roll forward to the next cycle.
Ledger events
Every detection that results in a CTA action creates a detection.converted ledger event. Use GET /v1/ledger to query your account's events and aggregate earnings.
Ledger entries are immutable. Once a detection event is recorded, it cannot be modified or deleted. Disputed events are flagged and reviewed, but the original record is preserved.
EMO Endpoints
Register an EMO
Create a new EMO by supplying a reference image and CTA configuration. The object is indexed asynchronously — poll status until active.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable label for this EMO. |
| image | file | Yes | Reference image (JPEG / PNG / WebP, min 800px). |
| ctaLabel | string | Yes | Button text shown to the end user on detection. |
| buyUrl | string | Yes | Destination URL triggered when CTA is tapped. |
| additionalImages | file[] | No | Up to 4 supplemental reference images for multi-angle coverage. |
| metadata | object | No | Arbitrary key-value pairs stored with the EMO (max 2KB). |
Retrieve an EMO
curl https://spatemo.com/v1/emos/emo_1a2b3c4d \
-H "Authorization: Bearer spat_live_..."
List EMOs
Returns a paginated list of your registered EMOs. Supports status filtering and cursor-based pagination via after.
| Query param | Default | Description |
|---|---|---|
| status | — | Filter by status: indexing, active, paused, archived. |
| limit | 20 | Results per page. Max 100. |
| after | — | Cursor for the next page (from pagination.next in prior response). |
Update an EMO
Update name, ctaLabel, buyUrl, status, or metadata. Reference images cannot be changed — archive and re-register to update the visual fingerprint.
Archive an EMO
Sets the EMO status to archived. It is removed from the detection index within 60 seconds. Ledger history is preserved.
Detection Endpoint
Submit an image frame for recognition. SPATEMO checks the frame against your active EMO index and returns the highest-confidence match above the threshold.
This endpoint is rate-limited by default to 60 requests/minute per API key. For high-frequency video streams, contact api@spatemo.com to discuss stream-optimized integration.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| image | file | Yes | Camera frame or image crop. JPEG / PNG / WebP. |
| sessionId | string | Recommended | Opaque per-session identifier for deduplication. |
| minConfidence | float | No | Minimum confidence score to return a match. Default: 0.90. Range: 0.50–1.00. |
| scopeEmoIds | string[] | No | Limit detection to a specific subset of your EMOs. |
Response fields
| Field | Type | Description |
|---|---|---|
| matched | boolean | Whether a match was found above the confidence threshold. |
| emoId | string | ID of the matched EMO. Present when matched is true. |
| confidence | float | Match confidence score between 0 and 1. |
| cta | object | CTA payload (label, url) from the matched EMO. |
| detectionId | string | Unique ID for this detection event. Logged in the royalty ledger. |
| latencyMs | integer | Server-side processing time in milliseconds. |
Ledger Endpoint
Query your royalty ledger. Returns a paginated list of detection events that resulted in CTA conversions, with per-event and aggregate earnings.
Query parameters
| Parameter | Description |
|---|---|
| emoId | Filter events for a specific EMO. |
| from | ISO 8601 datetime. Start of query window. |
| to | ISO 8601 datetime. End of query window. |
| limit | Results per page. Default 50, max 500. |
// Example response
{
"balance": {
"pending": 142.50,
"currency": "USD"
},
"events": [
{
"detectionId": "det_9z8y7x",
"emoId": "emo_1a2b3c4d",
"earnings": 0.42,
"role": "platform",
"timestamp": "2026-06-26T14:22:00Z"
}
],
"pagination": {
"total": 1204,
"next": "cur_abc123"
}
}
Webhooks
Register a webhook URL to receive real-time event notifications. SPATEMO will POST a signed JSON payload to your endpoint when events occur.
Registering a webhook
curl -X POST https://spatemo.com/v1/webhooks \
-H "Authorization: Bearer spat_live_..." \
-H "Content-Type: application/json" \
-d '{"url":"https://yourapp.com/hooks/spatemo","events":["detection.matched","detection.converted"]}'
Event types
| Event | Fired when |
|---|---|
| emo.active | An EMO finishes indexing and is ready for detection. |
| detection.matched | A detection request returns a match. |
| detection.converted | A matched CTA was acted upon by the end user. |
| ledger.settled | Monthly ledger settlement is complete. |
Verifying signatures
Each webhook request includes a Spatemo-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook secret. Always verify this before processing.
// Node.js verification example
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
Error Codes
SPATEMO uses conventional HTTP status codes. Error responses include a machine-readable code and a human-readable message.
{
"error": {
"code": "emo_not_found",
"message": "No EMO found with the provided ID.",
"status": 404
}
}
| Code | HTTP | Meaning |
|---|---|---|
| unauthorized | 401 | Missing or invalid API key. |
| forbidden | 403 | Key does not have permission for this operation. |
| emo_not_found | 404 | No EMO with the provided ID exists in your account. |
| emo_not_active | 409 | Detection requested against an EMO that is not yet active. |
| image_too_small | 422 | Submitted image is below the 800px minimum dimension. |
| image_unreadable | 422 | Image could not be decoded. Check format and file integrity. |
| confidence_below_threshold | 200 | Not an error — match exists but confidence did not meet threshold. Returned as matched: false. |
| rate_limit_exceeded | 429 | You have exceeded the rate limit for this endpoint. |
| internal_error | 500 | Something went wrong on our end. Contact api@spatemo.com with the request ID. |
Rate Limits
Limits are enforced per API key. Responses include rate limit headers on every request.
| Endpoint | Default limit |
|---|---|
| POST /v1/detect | 60 requests / minute |
| POST /v1/emos | 100 requests / hour |
| GET /v1/emos | 300 requests / minute |
| GET /v1/ledger | 60 requests / minute |
# Rate limit headers included in all responses
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1751234567
When a limit is exceeded, a 429 is returned with a Retry-After header indicating seconds until the window resets. Need higher limits? Contact api@spatemo.com.
Changelog
v1.0 — June 2026
Initial private beta release. EMO registration, detection, royalty ledger, and webhook endpoints available to approved developers.