AtlasAR Cloud

API reference · v1

AtlasAR Cloud API

Optimise and host true-size 3D models for the web and AR, turn product photos into models, and get notified when they are ready.

Introduction

The AtlasAR Cloud API turns 3D files and product photos into light, true-size models for the web and AR (Android Scene Viewer, iOS Quick Look, WebXR), hosts them on a CDN, and tells your server when they are ready. It is a REST API: resources have predictable URLs, requests and responses are JSON (model files are sent as their own bytes), and errors use standard HTTP status codes with a stable machine-readable code.

Only showing a model? You do not need the API for that. The viewer SDK shows any AtlasAR Cloud model with one script tag, without a key in the page.
Base URLhttps://cloud.wpaugmentedreality.com
Versionv1 in the path. Fields may be added to responses at any time; nothing is removed or renamed within v1.
FormatsJSON (UTF-8). Uploads: model/gltf-binary for GLB, image/jpeg|png|webp for photos.
TimeISO 8601, UTC.

Quick start

Optimise a GLB and get a hosted, true-size model in two calls: upload it, then read it back (or wait for the webhook).

Upload a GLB
# 1. Upload a GLB: made exactly 30 cm tall, optimised at the default level
curl -X POST "https://cloud.wpaugmentedreality.com/v1/models/upload?name=Vase&height_cm=30" \
  -H "Authorization: Bearer $ATLASAR_KEY" \
  -H "Content-Type: model/gltf-binary" \
  --data-binary @vase.glb

# 2. Read it until "status" is "ready" (or use a webhook)
curl "https://cloud.wpaugmentedreality.com/v1/models/mdl_9yX2…" -H "Authorization: Bearer $ATLASAR_KEY"

Authentication

Every /v1 request carries the site's API key as a bearer token. Keys look like ar_live_… (32 random characters) and are stored only as a SHA-256 hash: they are shown once, when created.

Header
Authorization: Bearer ar_live_Xq3…
  • One key per site. A key sees only its own site's models, webhook and statistics.
  • Server side only. Never put a key in browser code: the viewer SDK needs none.
  • Active or pending. A new email gets an active key at once. A new site for an existing account gets a pending key: every call answers 403 approval_required until the account owner approves the emailed link.
  • WordPress sites connect from AtlasAR › Integrations in the plugin, which registers the site and stores the key encrypted; no key needs to be copied.

Errors

Successful requests answer 2xx. Errors answer 4xx (your request) or 5xx (ours), always with the same body, so code can branch on error.code. The full list is under Error codes.

Error response · 422
{
  "error": {
    "code": "invalid_height",
    "message": "height_cm must be a number from 0.5 to 1000 (the real product height), or left out to keep the size of the model."
  }
}

Idempotency

Send an Idempotency-Key header (8–80 characters: letters, digits, _, -) with model-creating requests. A retry with the same key never creates or bills a second model: it answers 200 with the model of the first request.

Header
Idempotency-Key: order-1042-vase

Plans, limits and billing

Usage is counted in units: optimising a GLB costs 1 unit, photo to 3D costs 10. Models are billed on success: a request is accepted at 0 units (202) and billed only when its model is ready. A model that fails, or that you delete before it is ready, is never billed.

PlanOptimisations a monthHosted modelsFeatures
Free2010optimize, webhooks
Premium (an AtlasAR Pro licence seat on the site)No monthly limitNo limitoptimize, photo_to_3d, webhooks
  • At most 5 models per account are processed at a time (429 too_many_in_progress).
  • The monthly allowance is checked when a model is accepted, so the last accepted model may finish just past the limit.
  • Busy servers answer 503 server_busy with Retry-After; such requests are never billed.

Sites and usage

Register a site

POST/v1/projects/registerNo key

Creates a site (project) and its API key. With a new email, the account is created at once on the Free plan and the key is active. With the email of an existing account, the key is pending until the owner approves the site.

Body

NameDescription
platformrequired
string
wordpress or custom.
site_urlrequired
string
The site's public address, e.g. https://shop.example.
emailrequired
string
The account email. Approval links and receipts go there.
Request
curl -X POST "https://cloud.wpaugmentedreality.com/v1/projects/register" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "custom", "site_url": "https://shop.example", "email": "owner@shop.example" }' 
Response · 201
{
  "api_key": "ar_live_Xq3…",
  "key_prefix": "ar_live_Xq3",
  "project_id": 41,
  "plan": "free",
  "approval": "active"
}

A WordPress site already registered with another email answers 409 project_exists with a masked owner_hint. The plugin can then move it with a one-time proof read from the site itself (POST /v1/projects/takeover and /takeover/verify; public sites only).

Read usage

GET/v1/usageSite key

The site's plan and this month's units.

Request
curl "https://cloud.wpaugmentedreality.com/v1/usage" -H "Authorization: Bearer $ATLASAR_KEY"
Response · 200
{
  "usage": {
    "plan": "free",
    "period": "2026-10",
    "units_used": 7,
    "units_limit": 20,
    "units_remaining": 13,
    "resets_at": "2026-11-01T00:00:00Z"
  }
}

Models

The model object

FieldDescription
id
string
Starts with mdl_.
name
string
Your name for it (1–200 characters).
status
string
queued, processing, ready or failed.
height_cm
number | null
The real height it was made at; null = its own size.
source
string
glb or photo: what it was made from.
files.glb
string | null
When ready: the URL of the model file. Public, immutable, CORS *, served by the CDN.
stats
object
When ready: triangles, glb_mb, texture_px, texture_count, level.
budget
string
Page-weight grade: pass (≤ 3 MB), warn (≤ 5 MB) or fail.
viewer
object | null
Settings the viewer SDK applies (see Viewer settings).
error
object | null
When failed: { code }, e.g. too_complex.
created_at
string
ISO 8601.
expires_at
string | null
Only for temporary models (made on the Create page of the dashboard); API models never expire.

Optimise a GLB

POST/v1/models/uploadSite key

Optimises and hosts a GLB: real-world scale (optional), base on the floor, simplified to the level's triangle budget, textures resized to WebP, meshopt compression. Answers at once with the queued model; the work runs in the background.

Query parameters

NameDescription
namerequired
string
A name for the model (1–200 characters).
height_cmoptional
number
The product's real height, 0.5–1000. The model is scaled to it with its base on the floor. Left out, the model keeps its own size.
leveloptional
string
How far to optimise: maximum, high (default), balanced, low or minimum. See Optimisation levels.

Body

The GLB file (≤ 50 MB) with Content-Type: model/gltf-binary. Larger files: use a direct upload.

Request
curl -X POST "https://cloud.wpaugmentedreality.com/v1/models/upload?name=Vase&height_cm=30&level=balanced" \
  -H "Authorization: Bearer $ATLASAR_KEY" \
  -H "Content-Type: model/gltf-binary" \
  -H "Idempotency-Key: vase-2026-10" \
  --data-binary @vase.glb
Response · 202
{
  "id": "mdl_9yX2kQ4LrT8vN1pA6sZ3bW",
  "name": "Vase",
  "source": "glb",
  "status": "queued",
  "height_cm": 30,
  "files": null,
  "units": 0,
  "usage": { "plan": "free", "units_used": 7, "units_limit": 20, … }
}

Photo to 3D

Premium

POST/v1/modelsSite key

Makes a 3D model from one product photo with AI, then optimises and hosts it like a GLB. A photo model costs 10 units when it is ready.

Query parameters

NameDescription
namerequired
string
A name for the model.
height_cmrequired
number
The product's real height, 0.5–1000 (an AI model has no real size of its own).

Body

A JPEG, PNG or WebP photo (≤ 25 MB) with its Content-Type. A plain background and the whole product in view give the best result.

Request
curl -X POST "https://cloud.wpaugmentedreality.com/v1/models?name=Mug&height_cm=10" \
  -H "Authorization: Bearer $ATLASAR_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @mug.jpg

List models

GET/v1/modelsSite key

This site's models, newest first.

Query parameters

NameDescription
limitoptional
integer
1–100 (default 50).
beforeoptional
string
A model id: models older than it (from next_before).
Response · 200
{
  "data": [ { "id": "mdl_9yX2…", "name": "Vase", "status": "ready", … } ],
  "next_before": "mdl_7aB1…"
}

Retrieve a model

GET/v1/models/{id}Site key

One model. Poll it until status is ready or failed, or use a webhook instead.

Response · 200
{
  "id": "mdl_9yX2kQ4LrT8vN1pA6sZ3bW",
  "name": "Vase",
  "status": "ready",
  "height_cm": 30,
  "source": "glb",
  "files": { "glb": "https://cloud.wpaugmentedreality.com/files/p/41/mdl_9yX2kQ4LrT8vN1pA6sZ3bW/3f9c8a1d2b7e4f60.glb" },
  "stats": { "triangles": 29871, "glb_mb": 0.77, "texture_px": 1024, "texture_count": 2, "level": "balanced" },
  "budget": "pass",
  "viewer": null,
  "error": null,
  "created_at": "2026-10-18T09:12:44Z",
  "expires_at": null
}

The file you sent is deleted once the model is ready or has failed for good; the finished model stays until you delete it.

Save viewer settings

PUT/v1/models/{id}/viewerSite key

Settings the viewer SDK applies wherever the model is shown with <atlasar-viewer model="…">: change the title, background, AR placement or try-on once, and every site showing the model updates within a minute. Keys are the SDK's option names; null clears them.

Request body
{
  "viewer": {
    "title": "Oak chair",
    "background": "#f5f5f4",
    "arPlacement": "floor",
    "hotspots": [ { "position": "0 0.4 0.2", "normal": "0 1 0", "label": "Solid oak seat" } ]
  }
}

Unknown keys or invalid values: 400 invalid_viewer (64 KB at most). Premium settings (hotspots, dimensions, items, variants, tryonCalibration) are saved on any plan and shown while the site is Premium.

Delete a model

DELETE/v1/models/{id}Site key

Deletes the model and its files; its public URL answers 404 at once. A model deleted before it is ready is not billed.

Response · 200
{ "ok": true }

Optimisation levels

The level of an upload sets how far a model is simplified. high is the default and suits most product pages. The browser optimiser in the SDK uses the same table, so a model looks the same wherever it was made. A quality from 0–100 is also accepted (90+ maximum, 75+ high, 60+ balanced, 40+ low, below that minimum).

LevelTrianglesLargest textureImage qualityUse it for
maximum150,0004096 px95Close-up detail: jewellery, watches
high default50,0002048 px85Most products
balanced30,0001024 px80Large catalogues, faster pages
low15,0001024 px70Slow connections
minimum8,000512 px60Simple shapes, the lightest files
Decoding meshopt. Finished models use meshopt compression (smaller files, a 6 KB decoder). The viewer SDK and the WordPress plugin decode it already. With your own <model-viewer>, set self.ModelViewerElement = { meshoptDecoderLocation: '/js/meshopt_decoder.js' } before it loads; with three.js, GLTFLoader.setMeshoptDecoder(MeshoptDecoder).

Direct uploads

Large files go straight from your server or the browser to storage, never through the API. Three steps:

  1. Ask for an upload URL with the exact size and type.
  2. PUT the file to upload_url with exactly the headers given (type and size are signed).
  3. Create the model with POST /v1/models/upload (or /v1/models) and a JSON body { "upload_id", "name", "height_cm" }. An upload is used once, by the site that asked for it, within 15 minutes.

Create an upload

POST/v1/uploadsSite key

Body

NameDescription
kindrequired
string
glb or photo.
sizerequired
integer
The exact size in bytes.
content_typerequired
string
e.g. model/gltf-binary.
Response · 201
{
  "id": "upl_4Hq…",
  "upload_url": "https://cloud.wpaugmentedreality.com/…",
  "method": "PUT",
  "headers": { "Content-Type": "model/gltf-binary", "Content-Length": "48211330" },
  "expires_at": "2026-10-18T09:27:44Z"
}

20 open uploads per account at most.

Free 3D model library

Ready-made, optimised models published by AtlasAR (made by us, or CC0 / CC BY). Any site key may use them, on any plan, at no cost: nothing is metered.

Download a model

GET/v1/library/{id}/fileSite key

The GLB itself (model/gltf-binary, as an attachment). GET /v1/library/{id} returns one model's details. For CC BY models, show the credit.

200 downloads per site a day; then 429 rate_limited.

Viewer analytics

How shoppers use your models' 3D views on every website that shows them. The viewer SDK counts by itself for <atlasar-viewer model="mdl_…"> and sends only totals (no cookies, no IP address, no visitor or page data). A page can turn it off with analytics="none".

Events: view, turn, open, ar, tryon, tryon_sample, snapshot, and with Premium modules hotspot, other_model, variant. Daily totals are kept 400 days.

Read analytics

GET/v1/statsSite key

Totals for this site's models: by event, by day, and per model (most 3D views first).

Query parameters

NameDescription
daysoptional
integer
7, 30 (default), 90 or 365.
Response · 200
{
  "days": 30,
  "totals": { "view": 1840, "turn": 612, "ar": 233, "tryon": 41 },
  "daily": { "2026-10-17": { "view": 71, "ar": 9 } },
  "models": [ { "id": "mdl_9yX2…", "name": "Vase", "counts": { "view": 902, "ar": 120 } } ]
}

The SDK sends its counts to POST /sdk/v1/stats (public, CORS *); you never call it yourself.

Webhooks

AtlasAR Cloud calls your HTTPS endpoint when a model is ready or failed, so you never need to poll. One endpoint per site. Events: model.ready, model.failed (data.model is the model object) and ping.

Set the endpoint

PUT/v1/webhookSite key

Creates or changes the endpoint. The signing secret is shown once, when the endpoint is created.

Body

NameDescription
urlrequired
string
An https URL that resolves to public addresses only (checked when saved and at every send).
Response · 201
{ "url": "https://shop.example/atlasar-webhook", "secret": "whsec_…" }
AlsoDescription
GET /v1/webhookThe endpoint, the secret's prefix, when it was created.
POST /v1/webhook/rotate-secretA new secret; the old one stops at once.
POST /v1/webhook/testSends a ping (10 an hour).
GET /v1/webhook/deliveriesRecent deliveries: type, status, attempts, last HTTP status.
DELETE /v1/webhookRemoves the endpoint and cancels pending deliveries.

Verify a delivery

Each delivery carries AtlasAR-Event-Id, AtlasAR-Event-Type and AtlasAR-Signature: t=<unix time>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>.<raw body>" with your secret. Check it on the raw body, refuse deliveries older than 5 minutes, and answer 2xx within 10 seconds. Delivery is at-least-once: drop repeated event ids. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h; redirects are never followed.

Verify the signature
$body = file_get_contents( 'php://input' );
parse_str( str_replace( ',', '&', $_SERVER['HTTP_ATLASAR_SIGNATURE'] ?? '' ), $sig );
$fresh = isset( $sig['t'] ) && abs( time() - (int) $sig['t'] ) <= 300;
$valid = isset( $sig['v1'] ) && hash_equals( hash_hmac( 'sha256', $sig['t'] . '.' . $body, $secret ), $sig['v1'] );
if ( ! $fresh || ! $valid ) {
    http_response_code( 401 );
    exit;
}
$event = json_decode( $body, true ); // [ 'type' => 'model.ready', 'data' => [ 'model' => … ] ]

Error codes

CodeStatusMeaning
missing_api_key
invalid_api_key
401No key, or an unknown or revoked key.
approval_required403The site waits for the account owner's approval.
suspended403The site or account is suspended.
feature_not_in_plan403e.g. photo to 3D on the Free plan.
model_limit403The plan's hosted-model limit is reached.
not_found404No such model for this key.
upload_not_found
upload_incomplete
upload_size_mismatch
404 / 409Direct-upload problems.
project_exists409The site is registered with another email (see Register a site).
file_too_large413Over 50 MB (GLB) or 25 MB (photo).
unsupported_media_type415Wrong Content-Type.
invalid_name
invalid_height
invalid_level
invalid_idempotency_key
invalid_days
422A bad parameter; the message says which values are allowed.
not_glb
invalid_glb
invalid_gltf
external_reference
out_of_bounds
too_complex
no_geometry
unsupported_*
422The GLB was refused before processing (for a direct upload the model becomes failed with that code).
invalid_viewer400Unknown viewer setting or invalid value.
quota_exceeded429This month's units are used up (a usage block is included).
rate_limited429Too many requests; wait for Retry-After.
too_many_in_progress4295 models are already processing for this account.
server_busy503At capacity; retry after Retry-After. Never billed.
generation_unavailable503Photo to 3D is not available right now.