Public Developer API Reference
The OIM backend provides a clean, versioned REST API for compiling layout measurements, packaging signed interactive media assets, and retrieving public cryptographic keys.
Base URL: https://api.oim.media
1. Authentication and API Keys
| Endpoint | Key | Limit |
|---|---|---|
POST /v1/oim/package | Required (401 OIM_E_AUTH_REQUIRED without it) | 30 / minute per IP, plus the monthly signing quota |
POST /v1/oim/compile | Optional | 60 / minute per IP |
POST /v1/oim/extract | No | 30 / minute per IP |
GET /v1/media/keys | No | — |
API keys are exclusive to the Enterprise plan; a key from any other plan is rejected with 403. Send the key in a header:
X-API-Key: YOUR_DEVELOPER_KEYor as a bearer token:
Authorization: Bearer YOUR_DEVELOPER_KEYKeep the key on your server
The key signs against your quota. Do not put it in browser code or in an app you distribute, and do not send it in the URL (?api_key=): URLs end up in logs and histories. Call the API from your backend.
Avoid requests that will fail
- Do not call
/packagewithout a key: it always answers401. - Validate before signing:
400and403rejections arrive before any quota is used, but they count towards the per-minute limit. - On
429, wait the seconds inRetry-Afterbefore retrying; retrying in a loop extends the block. - To retry a signature after a network drop, use
Idempotency-Key(see below): it is not charged twice.
Rate limits: RateLimit-* and Retry-After
Rate-limited endpoints answer with RateLimit-Limit (requests allowed in the window), RateLimit-Remaining (left in this window) and RateLimit-Reset (seconds until the window resets). Past the limit the response is 429 with Retry-After: the seconds to wait before retrying.
Tracing a request: X-Request-ID
Every response carries an X-Request-ID header. If something fails, include it when you report the problem: it identifies your request in our logs. You can also send your own X-Request-ID (8 to 128 characters of A-Z, a-z, 0-9, ., _ or -) and the response returns it unchanged; any other value is replaced with a new one.
Security headers
Every response carries defensive security headers:
Cross-Origin-Resource-Policy: cross-originStrict-Transport-Security: max-age=63072000; includeSubDomainsX-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: no-referrer
Request payload limits
- Non-upload JSON endpoints accept payloads up to 5 MB. Chunked transfer encoding is not supported on non-upload endpoints and is rejected with
400 Bad Request. - Upload and compile endpoints accept files up to 50 MB (
MAX_UPLOAD_SIZE). Base images are capped at 8192 px per side and 16 MP (16,777,216 pixels total).
2. Compile Measurements: POST /v1/oim/compile
Converts raw screen pixel coordinates into container-relative adaptive percentages and cqw units.
- Endpoint:
POST /v1/oim/compile - Rate Limit: 60 requests / minute
- Request Body (
application/json):
{
"canvas_width": 1080,
"canvas_height": 1080,
"slots_raw": [
{
"id": "hero_text",
"type": "text",
"x_px": 108,
"y_px": 216,
"width_px": 864,
"height_px": 120,
"content": {
"en": "Empower your media",
"es": "Potencia tus medios"
}
}
]
}- Response (
200 OK): Returns normalizedOimMetadatawith coordinates scaled to container query widths.
3. Package & Sign Asset: POST /v1/oim/package
Combines a clean baked PNG image (Layer −1) with compiled metadata, signs the payload with ECDSA P-256, and generates the final .oim.png binary.
- Endpoint:
POST /v1/oim/package - Authentication: API key required
- Rate Limit: 30 requests / minute, plus the monthly signing quota
- Request Format:
multipart/form-data - Parameters:
file: PNG image file binary (baked image, Layer −1). Up to 50 MB, at most 8192 px per side, and at most 16,777,216 pixels (16 MP). Exceeding image bounds returns400 Bad Request.metadata: Serialized JSON string matchingOimMetadataSchema.
curl -X POST https://api.oim.media/v1/oim/package \
-H "X-API-Key: $OIM_API_KEY" \
-F "[email protected]" \
-F "[email protected]" \
--output result.oim.pngResponse (
200 OK): Binaryimage/pngstream containing the embedded, signedOIM_Datachunk.Account refusals (
403, also on/v1/oim/compile): nothing is signed and no quota is used.codeMeaning OIM_E_POLICIES_PENDINGThe account has new Terms or Privacy Policy already in force and not accepted. Accept them at https://oim.media/#/politicasand retry.
Retrying without paying twice: Idempotency-Key
Each signature uses one unit of the monthly quota. If the connection drops after the server signed, a retry would be charged again. To avoid that, send an Idempotency-Key header with a value you generate once per request, a UUID for example, and send the same value when you retry that request.
- Within 24 hours, a retry with the same key and the same content is signed again without using quota.
- The same key with different content is refused with
422. Use a new key for every new file. - A malformed key, outside 16–128 characters of
A-Z a-z 0-9 _ -, is refused with400. - The header is optional: without it, every request is charged as before.
curl -X POST https://api.oim.media/v1/oim/package \
-H "X-API-Key: $OIM_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "[email protected]" \
-F "[email protected]" \
--output result.oim.pngA lighter base image: base_optimization
The server always stores the base image without an unused transparency channel: an opaque RGBA base becomes RGB, with identical pixels. An optional form field asks for more:
| Value | What it does |
|---|---|
lossless (default) | Identical pixels. Only the unused transparency is dropped. |
palette | Reduces an opaque base to 256 colours. Much smaller (around 60% on large illustrations) but the pixels change: gradients may show slight banding. |
- A base with real transparency is never reduced to a palette: it is stored lossless.
- The
X-OIM-Base-Optimizationresponse header tells which one was applied. - Any other value is refused with
400and the codeOIM_E_BASE_OPTIMIZATION_INVALID, before any quota is used.
Text marks
A text whose mark tags are malformed (an unknown mark, an unclosed or nested tag, a single brace that is not a tag; see Text marks in the format specification) is refused with 400 and the code OIM_E_TEXT_MARKS_INVALID, naming the layer and the language, before any quota is used. A signed .oim therefore only carries well-formed texts.
- With
Idempotency-Key, the option is part of the content: the same key with another option is refused with422.
curl -X POST https://api.oim.media/v1/oim/package \
-H "X-API-Key: $OIM_API_KEY" \
-F "[email protected]" \
-F "[email protected]" \
-F "base_optimization=palette" \
--output result.oim.png4. Extract Metadata: POST /v1/oim/extract
Reads and validates an existing .oim.png asset, extracting both the uncompressed metadata JSON and the clean baked base image (Layer −1).
Endpoint:
POST /v1/oim/extractRequest Format:
multipart/form-dataParameters:
file: Uploaded.oim.pngbinary (PNG only, up to 50 MB).
Rate Limit: 30 requests / minute
Response (
200 OK):
{
"image_base64": "iVBORw0KGgo...",
"metadata": { "version": "1.27.2", "signature": "MEQCIF+...", "slots": [] },
"revoked": false
}metadata holds the file's JSON as is, or { "error": ..., "slots": [] } when the PNG carries no OIM data. revoked is true when the file has been revoked.
5. Public Cryptographic Keys: GET /v1/media/keys
Exposes active and historical public verification keys in standard JSON Web Key (JWK) format.
- Endpoint:
GET /v1/media/keys - Authentication: Public (no key required)
- Response (
200 OK):
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "f83OJ3D2xFmT4v7Z2V4s8uY9xQ7...",
"y": "x_da7W8mP2zQ9v4L1m3s8uY9xQ7...",
"kid": "v1",
"use": "sig"
}
]
}Client players and independent auditors use these JWK keys to mathematically verify signature validity without connecting to private infrastructure.