Polycam Enterprise API v1

Programmatic access to your Polycam workspace — captures, 3D models, and metadata.

The Polycam Enterprise API lets you integrate Polycam data into your own applications and workflows. You can create captures, trigger cloud reconstruction, list and retrieve captures, download artifacts, export to additional mesh formats (OBJ, FBX, STL, and more) and floorplan formats (PDF, SVG, DXF, CSV reports, and more), inspect processing status, receive real-time webhook notifications when captures change, and manage API tokens.

Enterprise Feature The API is available to workspaces with an active Enterprise plan that includes the api add-on.

Base URL

All API v1 endpoints are served under:

https://poly.cam/api/v1

Token management endpoints (create, list, revoke) live on the main application API under a separate path:

https://poly.cam/api/account/api-tokens

Authentication

API Token Authentication (Capture Endpoints)

All capture endpoints require a bearer token in the Authorization header. Tokens are prefixed with poly_ followed by 64 hexadecimal characters.

Authorization: Bearer poly_abc123...def456

Key facts about API tokens:

Firebase User Authentication (Token Management Endpoints)

The token management endpoints (create / list / revoke) use Firebase authentication. Pass a valid Firebase ID token in the Authorization header:

Authorization: Bearer <firebase-id-token>

The authenticated user must be the owner of the target workspace (for personal workspaces) or have owner-level access to the organization.

Errors

The API uses conventional HTTP status codes and returns errors as JSON:

{
  "error": "Human-readable error message"
}
StatusMeaning
200Success
202Accepted — request accepted, processing asynchronously
400Bad Request — invalid parameters or body
401Unauthorized — missing or invalid authentication
403Forbidden — valid credentials but insufficient permissions
404Not Found — resource does not exist or is not in your workspace
409Conflict — a conflicting operation is already in progress
422Unprocessable Entity — resource exists but cannot be processed as requested
429Too Many Requests — rate limit or concurrent job limit exceeded
500Internal Server Error

Common 401 error messages

MessageCause
Missing Authorization headerNo Authorization header sent
Bearer authorization requiredHeader doesn't start with Bearer
Invalid API token formatToken doesn't start with poly_
Invalid API tokenToken not recognized
API token has been revokedToken was previously revoked
API token has expiredToken's expiration date has passed

Pagination

List endpoints return paginated results. Use the cursor value from a response to fetch the next page.

# First page
GET /v1/captures?limit=25

# Next page — use the cursor from the previous response
GET /v1/captures?limit=25&cursor=1706140800000

When there are no more results, the cursor field is omitted from the response.

Token Management

These endpoints let you create, list, and revoke API tokens for a workspace. They use Firebase user authentication (not API token auth).

Create an API Token

POST /api/account/api-tokens Firebase Auth

Creates a new API token for the specified workspace. The raw token value is returned only in this response — store it immediately.

Request Body

FieldTypeRequiredDescription
workspace object Yes Target workspace. Contains id (string) and type ("user" or "org").
name string Yes Human-readable label (1–100 characters).
expiresInDays number No Number of days until the token expires. Omit for a non-expiring token.

Example Request

curl -X POST https://poly.cam/api/account/api-tokens \
  -H "Authorization: Bearer <firebase-id-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace": { "id": "ws_abc123", "type": "org" },
    "name": "CI Pipeline Token",
    "expiresInDays": 90
  }'

Response 200

{
  "token": "poly_a1b2c3d4e5f6...64 hex chars",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "CI Pipeline Token",
  "prefix": "poly_a1b2c3d4",
  "createdAt": 1706140800000,
  "expiresAt": 1713916800000,
  "message": "Save this token now - it will not be shown again!"
}
Important The token field contains the full raw token. This is the only time it will be returned. Store it in a secure location (e.g. a secrets manager).

Error Responses

StatusReason
400Invalid user account
401Missing or invalid Firebase authentication
403User does not have permission to create tokens for this workspace

List API Tokens

GET /api/account/api-tokens Firebase Auth

Lists all API tokens for a workspace. Raw token values are never returned — only the prefix (first 13 characters) is shown.

Query Parameters

ParameterTypeRequiredDescription
workspaceId string Yes The workspace ID.
workspaceType string Yes "user" or "org"

Example Request

curl https://poly.cam/api/account/api-tokens?workspaceId=ws_abc123&workspaceType=org \
  -H "Authorization: Bearer <firebase-id-token>"

Response 200

{
  "tokens": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "CI Pipeline Token",
      "prefix": "poly_a1b2c3d4",
      "createdAt": 1706140800000,
      "createdBy": {
        "id": "user_xyz",
        "username": "alice"
      },
      "expiresAt": 1713916800000,
      "revoked": false,
      "revokedAt": undefined
    }
  ]
}

Error Responses

StatusReason
401Missing or invalid Firebase authentication
403User does not have permission to view tokens for this workspace

Revoke an API Token

DELETE /api/account/api-tokens/:tokenId Firebase Auth

Revokes an API token. Revoked tokens can no longer be used for authentication. This is a soft delete — the token record is preserved with a revokedAt timestamp.

Path Parameters

ParameterTypeDescription
tokenId string The ID of the token to revoke.

Example Request

curl -X DELETE https://poly.cam/api/account/api-tokens/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <firebase-id-token>"

Response 200

{
  "success": true,
  "message": "Token revoked"
}

If the token was already revoked:

{
  "success": true,
  "message": "Token already revoked"
}

Error Responses

StatusReason
400Invalid user account
401Missing or invalid Firebase authentication
403User does not have permission to revoke this token
404Token not found

Credits

Credits endpoints let API-token callers inspect the prepaid balance for the token's workspace and review the ledger of deposits, charges, and refunds. These endpoints are read-only and use API token authentication.

Get Credit Balance

GET /v1/credits API Token

Returns the current prepaid API credit balance for the authenticated token's workspace.

Example Request

curl https://poly.cam/api/v1/credits \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "balanceCredits": 1500,
  "updatedAt": 1706140800000
}

Error Responses

StatusReason
401Missing or invalid API token

Get Credit Ledger

GET /v1/credits/ledger API Token

Returns a paginated ledger of credit changes for the authenticated token's workspace, ordered by creation time with newest entries first.

Query Parameters

ParameterTypeDefaultDescription
limit integer 50 Number of ledger entries per page. Min: 1, Max: 200.
cursor string Pagination cursor from a previous response's cursor field.

Ledger Entry Fields

FieldDescription
kind"deposit", "charge", "refund", or "adjustment".
amountCreditsPositive for deposits/refunds; negative for charges.
balanceAfterCreditsWorkspace balance after this ledger entry was applied.
endpointAPI endpoint associated with a charge or refund, when applicable.
referenceIdInternal audit/debug reference, such as a job id or original ledger entry id, when applicable.

Example Request

curl https://poly.cam/api/v1/credits/ledger?limit=25 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "entries": [
    {
      "id": "ledger_abc123",
      "kind": "charge",
      "amountCredits": -1050,
      "balanceAfterCredits": 450,
      "createdAt": 1706140800000,
      "endpoint": "POST /api/v1/captures",
      "description": "Reconstruction (space)"
    },
    {
      "id": "ledger_def456",
      "kind": "deposit",
      "amountCredits": 1500,
      "balanceAfterCredits": 1500,
      "createdAt": 1706137200000,
      "description": "Credit purchase"
    }
  ],
  "cursor": "1706137200000"
}

When there are no more entries, the cursor field is omitted from the response.

Error Responses

StatusReason
401Missing or invalid API token

Captures

Capture endpoints let you create, list, and retrieve 3D capture metadata from your workspace. All capture endpoints require API token authentication. Creating captures additionally requires the reconstruct scope on the API token and reserves a reconstruction job, charged against your prepaid API credit balance.

List Captures

GET /v1/captures API Token

Returns a paginated list of captures in the authenticated workspace, ordered by creation date (newest first).

Query Parameters

ParameterTypeDefaultDescription
limit integer 20 Number of captures per page. Min: 1, Max: 100.
cursor string Pagination cursor from a previous response's cursor field.
externalId string Filter captures by external reference ID. Only captures with a matching externalId are returned.
hasExternalId string Filter by whether externalId is set. Use "true" to return only captures that have an externalId, or "false" to return only captures without one.
createdAfter integer Only return captures created after this timestamp (epoch milliseconds, exclusive).
createdBefore integer Only return captures created before this timestamp (epoch milliseconds, exclusive).
tag string Filter to captures that contain this tag.

Example Request

curl https://poly.cam/api/v1/captures?limit=10 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "captures": [
    {
      "id": "cap_abc123",
      "name": "Office Scan",
      "description": "3rd floor conference room",
      "tags": ["office", "interior"],
      "createdAt": 1706140800000,
      "updatedAt": 1706141400000,
      "visibility": "private",
      "session": {
        "mode": "lidar",
        "device": "iPhone 15 Pro",
        "appVersion": "3.5.0",
        "duration": 120,
        "imported": false,
        "drone": false
      },
      "processing": {
        "status": "completed",
        "sessionDuration": 120,
        "processingDuration": 45,
        "processingVersion": "2.1.0"
      },
      "mesh": {
        "vertexCount": 150000,
        "faceCount": 300000,
        "bboxSize": [5.2, 3.1, 4.8],
        "bboxCenter": [0.0, 1.5, 0.0]
      },
      "geoData": { /* location data, if not hidden */ },
      "availableArtifacts": [
        "thumbnail.jpg",
        "video.mp4",
        "skybox.jpg",
        "original.gltf",
        "original_geometry.bin",
        "textures/0.jpg",
        "textures/1.jpg"
      ],
      "thumbnail": "https://storage.example.com/captures/cap_abc123/thumbnail.jpg",
      "externalId": "MH-2024-456"
    }
  ],
  "cursor": "1706140800000"
}

When cursor is present in the response, pass it as a query parameter to retrieve the next page. When omitted, you've reached the end.

Error Responses

StatusReason
401Missing or invalid API token
500Internal server error

Get a Capture

GET /v1/captures/:captureId API Token

Returns metadata for a single capture. The capture must belong to the workspace associated with your API token.

Path Parameters

ParameterTypeDescription
captureId string The unique ID of the capture.

Example Request

curl https://poly.cam/api/v1/captures/cap_abc123 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "capture": {
    "id": "cap_abc123",
    "name": "Office Scan",
    // ... same shape as objects in the list response
  }
}

Error Responses

StatusReason
401Missing or invalid API token
404Capture not found or does not belong to your workspace
500Internal server error
Security note Requesting a capture that exists but belongs to a different workspace returns 404 (not 403) to avoid leaking the existence of resources in other workspaces.

Create a Capture

POST /v1/captures API Token

Creates a new capture, reserves its reconstruction job in waitingForUpload, and returns a signed upload URL for the session.zip file. Upload to this URL, then call Confirm session.zip Upload to make the job runnable.

Scope required This endpoint requires the reconstruct scope on the API token.

Request Body

FieldTypeRequiredDescription
name string No Display name for the capture (max 256 characters). Defaults to empty string.
externalId string No External reference ID for linking to external systems (max 256 characters).
mode string Yes Reconstruction mode: "photo", "space", or "splat".
numKeyframes integer Required for space and splat Number of keyframes for the reconstruction. Must be a positive integer.

Example Request

curl -X POST https://poly.cam/api/v1/captures \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Office Scan", "externalId": "JOB-123", "mode": "photo" }'

Response 201

{
  "capture": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Office Scan",
    "externalId": "JOB-123",
    // ... same shape as the Capture Object
  },
  "jobId": "7f4f2b6c-0a5d-4b5a-9f1f-0f3d0d3f9a12",
  "mode": "photo",
  "status": "pending",
  "upload": {
    "type": "simple",
    "session": "https://storage.example.com/signed-upload-url?token=..."
  },
  "charge": {
    "amountCredits": 500,
    "ledgerEntryId": "ledger_abc123"
  }
}

PUT your session.zip file to the upload.session URL.

Error Responses

StatusReason
400Invalid or missing mode, invalid numKeyframes, name exceeds 256 characters, or externalId exceeds 256 characters
401Missing or invalid API token
402Insufficient API credits
403Token does not have the reconstruct scope
429Rate limit or concurrent job limit exceeded (see Rate Limits)

Update a Capture

PATCH /v1/captures/:captureId API Token

Updates fields on a capture. Supports setting or clearing the name, description, tags, and externalId fields. At least one field must be provided.

Path Parameters

ParameterTypeDescription
captureId string The unique ID of the capture to update.

Request Body

FieldTypeRequiredDescription
name string No Display name (max 256 characters). Must not be empty.
description string No Description text (max 500 characters). Send an empty string "" to clear.
tags string[] No Array of tags (max 50 tags, each max 128 characters). Send an empty array [] to clear.
externalId string No External reference ID (max 256 characters). Send an empty string "" to clear.

Example Request — Update name and tags

curl -X PATCH https://poly.cam/api/v1/captures/cap_abc123 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Office Scan - Floor 3",
    "tags": ["office", "interior", "floor-3"]
  }'

Example Request — Set external ID

curl -X PATCH https://poly.cam/api/v1/captures/cap_abc123 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{ "externalId": "MH-2024-456" }'

Example Request — Clear external ID

curl -X PATCH https://poly.cam/api/v1/captures/cap_abc123 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{ "externalId": "" }'

Response 200

{
  "capture": {
    "id": "cap_abc123",
    "name": "Office Scan",
    "externalId": "MH-2024-456",
    // ... same shape as the Capture Object
  }
}

Error Responses

StatusReason
400No update fields provided, empty name, or field exceeds length limit
401Missing or invalid API token
404Capture not found or does not belong to your workspace

Session Upload

Creating a capture reserves reconstruction in waitingForUpload. Upload session.zip to the signed URL returned by Create Capture, then confirm the upload to make the job runnable. Use webhooks or poll the capture to track processing.

Splat mode requires Polycam frames.json inside session.zip Gaussian splatting needs per-photo camera poses and intrinsics. The archive must contain a Polycam-format frames.json file at the zip root, alongside the keyframes/ directory. Put the source photos under keyframes/images/, and make each frame's name match the photo filename. This is Polycam's own format, not COLMAP or NeRF transforms.json. Photo and space modes do not require this file.

Refresh session.zip Upload URL

POST /v1/captures/:captureId/session.zip API Token

Returns a fresh signed upload URL for the capture's session.zip. Use this if the original URL expires or an upload needs to be retried.

Scope required This endpoint requires the reconstruct scope on the API token.

Example Request

curl -X POST https://poly.cam/api/v1/captures/550e8400-e29b-41d4-a716-446655440000/session.zip \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "url": "https://storage.example.com/signed-upload-url?token=..."
}

Error Responses

StatusReason
401Missing or invalid API token
403Token does not have the reconstruct scope
404Capture not found or does not belong to your workspace

Confirm session.zip Upload

PUT /v1/captures/:captureId/session.zip API Token

Confirms that the capture's session.zip upload has completed. This records the upload metadata and wakes the reconstruction job that was waiting for the file. Polycam also confirms uploads server-side when storage reports the object was created, so this endpoint is an explicit fast path rather than the only way processing can start.

Scope required This endpoint requires the reconstruct scope on the API token.

Example Request

curl -X PUT https://poly.cam/api/v1/captures/550e8400-e29b-41d4-a716-446655440000/session.zip \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "sessionZip": {
    "size": 104857600,
    "timestamp": 1706140800000,
    "md5": "abc123..."
  }
}

Error Responses

StatusReason
401Missing or invalid API token
403Token does not have the reconstruct scope
404Capture not found, or session.zip has not been uploaded yet

Webhooks

Webhooks let Polycam notify your application in real time when captures in your workspace change, so you don't have to poll. You register one or more HTTPS endpoints, choose which event types you care about, and Polycam sends an HTTP POST with a JSON payload each time a matching event occurs.

Every delivery is signed with an HMAC-SHA256 signature derived from a per-endpoint signing secret so you can verify it genuinely came from Polycam. Failed deliveries are retried automatically with exponential backoff, and you can inspect event history and delivery attempts (or manually redeliver) via the webhook events endpoints.

Authentication All webhook management endpoints use the same API token authentication as the rest of the API. No special token scope is required. Endpoints are scoped to the token's workspace.

Event Types

An endpoint subscribes to one or more of the following event types:

EventFires when
capture.createdA new capture is created in the workspace.
capture.updatedAn existing capture changes in any way.
capture.deletedA capture is deleted from the workspace.
capture.updated is not a "processing complete" event It fires on every change to a capture — including metadata edits, status changes, and artifact updates — not just when reconstruction finishes. To detect that processing has completed, inspect capture.processing.status (and, when present, capture.processing.job.status) on the payload's Capture Object.

Payload

The request body is a JSON object with the event type and the full Capture Object the event relates to. For capture.deleted, the capture object reflects the capture's state immediately before deletion.

FieldTypeDescription
eventstringOne of the event types above.
captureobjectThe full Capture Object.

Example Payload

{
  "event": "capture.updated",
  "capture": {
    "id": "cap_abc123",
    "name": "Office Scan",
    "processing": {
      "status": "completed",
      "job": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "status": "completed"
      }
    }
    // ... full Capture Object
  }
}

Verifying Signatures

Each delivery includes the following HTTP headers:

HeaderDescription
Content-TypeAlways application/json.
X-Polycam-SignatureSignature in the form sha256=<hex>, an HMAC-SHA256 of the raw request body keyed with the endpoint's signing secret.
X-Polycam-Event-IdThe ID of the webhook event being delivered. The same event ID is reused across retries and redeliveries, which makes it useful for idempotency / deduplication.

To verify a delivery, compute the HMAC-SHA256 of the exact raw request body using your endpoint's signingSecret, then compare it (using a constant-time comparison) against the hex value in the X-Polycam-Signature header:

const crypto = require('crypto');

function verifyPolycamSignature(rawBody, signatureHeader, signingSecret) {
  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(rawBody) // the raw bytes of the request body
    .digest('hex');
  const received = signatureHeader.replace('sha256=', '');
  const expectedBuf = Buffer.from(expected);
  const receivedBuf = Buffer.from(received);
  if (expectedBuf.length !== receivedBuf.length) {
    return false;
  }
  return crypto.timingSafeEqual(expectedBuf, receivedBuf);
}
Respond quickly with a 2xx A delivery is considered successful only if your endpoint responds with a 2xx status code within 10 seconds. Any other status code, a timeout, or a connection error is treated as a failure and scheduled for retry. Do heavy work asynchronously and return 200 immediately.

Delivery & Retries

Each event is delivered independently to every enabled endpoint subscribed to its event type. If a delivery fails, Polycam retries up to 8 total attempts with exponential backoff. The wait before each retry follows this schedule:

RetryWait before attempt
1st retry30 seconds
2nd retry1 minute
3rd retry5 minutes
4th retry30 minutes
5th retry2 hours
6th retry8 hours
7th retry24 hours

After the 8th failed attempt the delivery is marked failed and no longer retried. You can inspect the status of every attempt via List Deliveries, and trigger a fresh set of deliveries with Redeliver Event. Captures of the response body are truncated to 1024 characters.

Create Endpoint

POST /v1/webhooks API Token

Registers a new webhook endpoint and generates a signing secret for it.

Request Body

FieldTypeRequiredDescription
urlstringYesThe HTTPS URL to deliver events to. Must use https://.
eventsstring[]YesNon-empty array of event types to subscribe to.

Example Request

curl -X POST https://poly.cam/api/v1/webhooks \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/hooks/polycam", "events": ["capture.created", "capture.updated"] }'

Response 201

{
  "webhook": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "url": "https://example.com/hooks/polycam",
    "events": ["capture.created", "capture.updated"],
    "signingSecret": "3f9a...<64 hex chars>",
    "enabled": true,
    "createdAt": 1706140800000,
    "updatedAt": 1706140800000
  }
}
Store the signing secret The signingSecret is returned on create, on Get Endpoint, and on Rotate Secret — but it is not included in the list or update responses. You need it to verify signatures, so store it securely.

Error Responses

StatusReason
400Invalid URL format, URL is not HTTPS, or events is empty / contains an unknown event type
401Missing or invalid API token

List Endpoints

GET /v1/webhooks API Token

Lists all webhook endpoints registered for your workspace. Signing secrets are omitted.

Example Request

curl https://poly.cam/api/v1/webhooks \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "webhooks": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "url": "https://example.com/hooks/polycam",
      "events": ["capture.created", "capture.updated"],
      "enabled": true,
      "createdAt": 1706140800000,
      "updatedAt": 1706140800000
    }
  ]
}

Get Endpoint

GET /v1/webhooks/:webhookId API Token

Retrieves a single webhook endpoint, including its signingSecret.

Path Parameters

ParameterTypeDescription
webhookIdstringThe ID of the webhook endpoint.

Example Request

curl https://poly.cam/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "webhook": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "url": "https://example.com/hooks/polycam",
    "events": ["capture.created", "capture.updated"],
    "signingSecret": "3f9a...<64 hex chars>",
    "enabled": true,
    "createdAt": 1706140800000,
    "updatedAt": 1706140800000
  }
}

Error Responses

StatusReason
401Missing or invalid API token
404Webhook not found or does not belong to your workspace

Update Endpoint

PATCH /v1/webhooks/:webhookId API Token

Updates a webhook endpoint. All fields are optional; only the fields you provide are changed. Use enabled: false to temporarily pause deliveries without deleting the endpoint.

Request Body

FieldTypeDescription
urlstring?New HTTPS delivery URL.
eventsstring[]?New non-empty array of event types to subscribe to.
enabledboolean?Whether the endpoint is active.

Example Request

curl -X PATCH https://poly.cam/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{ "events": ["capture.created", "capture.updated", "capture.deleted"], "enabled": false }'

Response 200

Returns the updated endpoint (without signingSecret), same shape as List Endpoints items.

Error Responses

StatusReason
400Invalid URL format, URL is not HTTPS, or events provided but empty
401Missing or invalid API token
404Webhook not found or does not belong to your workspace

Delete Endpoint

DELETE /v1/webhooks/:webhookId API Token

Permanently removes a webhook endpoint. No further events are delivered to it.

Example Request

curl -X DELETE https://poly.cam/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{ "deleted": true }

Error Responses

StatusReason
401Missing or invalid API token
404Webhook not found or does not belong to your workspace

Rotate Secret

POST /v1/webhooks/:webhookId/rotate-secret API Token

Generates a new signing secret for the endpoint and returns it. The previous secret stops being valid immediately, so update your verification code to use the new secret.

Example Request

curl -X POST https://poly.cam/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000/rotate-secret \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "webhook": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "url": "https://example.com/hooks/polycam",
    "events": ["capture.created", "capture.updated"],
    "signingSecret": "<new 64 hex chars>",
    "enabled": true,
    "createdAt": 1706140800000,
    "updatedAt": 1706227200000
  }
}

Error Responses

StatusReason
401Missing or invalid API token
404Webhook not found or does not belong to your workspace

List Events

GET /v1/webhook-events API Token

Lists webhook events recorded for your workspace, most recent first. An event is recorded whenever a matching capture change occurs and at least one enabled endpoint is subscribed to its type. Useful for auditing and debugging.

Query Parameters

ParameterTypeDescription
limitintegerNumber of events to return. Clamped to the range 1–100. Defaults to 20.
cursorstringPagination cursor returned by a previous response. See Pagination.
eventstringFilter to a single event type.

Example Request

curl "https://poly.cam/api/v1/webhook-events?limit=20&event=capture.updated" \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "events": [
    {
      "id": "e1f2a3b4-...",
      "payload": {
        "event": "capture.updated",
        "capture": { "id": "cap_abc123" /* ... full Capture Object */ }
      },
      "createdAt": 1706140800000
    }
  ],
  "cursor": "1706140700000"
}

When cursor is present, pass it back as the cursor query parameter to fetch the next page. When it is absent, there are no more results.

Get Event

GET /v1/webhook-events/:eventId API Token

Retrieves a single webhook event, including its full payload.

Example Request

curl https://poly.cam/api/v1/webhook-events/e1f2a3b4-... \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "event": {
    "id": "e1f2a3b4-...",
    "payload": { "event": "capture.updated", "capture": { /* ... */ } },
    "createdAt": 1706140800000
  }
}

Error Responses

StatusReason
401Missing or invalid API token
404Event not found or does not belong to your workspace

List Deliveries

GET /v1/webhook-events/:eventId/deliveries API Token

Lists the delivery attempts for a single event — one delivery record per subscribed endpoint. Use this to debug failing endpoints.

Example Request

curl https://poly.cam/api/v1/webhook-events/e1f2a3b4-.../deliveries \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "deliveries": [
    {
      "id": "d1e2f3...",
      "eventId": "e1f2a3b4-...",
      "endpointId": "550e8400-...",
      "status": "delivered",
      "attempts": 1,
      "maxAttempts": 8,
      "lastAttemptAt": 1706140801000,
      "nextRetryAt": null,
      "httpStatus": 200,
      "responseBody": "ok",
      "error": null,
      "createdAt": 1706140800000,
      "completedAt": 1706140801000
    }
  ]
}

Delivery Fields

FieldTypeDescription
idstringUnique delivery identifier.
eventIdstringThe webhook event being delivered.
endpointIdstringThe endpoint this delivery targets.
statusstringpending, delivering, delivered, or failed.
attemptsintegerNumber of attempts made so far.
maxAttemptsintegerMaximum attempts before giving up (8).
lastAttemptAtnumber?Unix timestamp (ms) of the most recent attempt.
nextRetryAtnumber?Unix timestamp (ms) of the next scheduled retry, if pending.
httpStatusinteger?HTTP status code returned by your endpoint on the last attempt.
responseBodystring?Response body from your endpoint, truncated to 1024 characters.
errorstring?Error message for transport-level failures (e.g. timeout, DNS error).
createdAtnumberUnix timestamp (ms) when the delivery was created.
completedAtnumber?Unix timestamp (ms) when the delivery reached a terminal state.

Error Responses

StatusReason
401Missing or invalid API token
404Event not found or does not belong to your workspace

Redeliver Event

POST /v1/webhook-events/:eventId/redeliver API Token

Queues a fresh set of deliveries for an event to every currently enabled endpoint subscribed to the event's type. Useful for replaying an event after fixing a broken endpoint. The original event ID (and therefore the X-Polycam-Event-Id header) is reused.

Example Request

curl -X POST https://poly.cam/api/v1/webhook-events/e1f2a3b4-.../redeliver \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "redelivered": true,
  "deliveryCount": 2
}

Error Responses

StatusReason
401Missing or invalid API token
404Event not found or does not belong to your workspace
422No enabled endpoints are subscribed to this event's type

Rate Limits

The API enforces two types of limits on reconstruction jobs to ensure fair usage and system stability:

LimitDescriptionDefault
Daily rate limit Maximum number of reconstruction jobs that can be reserved within a rolling 24-hour window. 25 per 24 hours
Concurrent limit Maximum number of reconstruction jobs that can be active at the same time, including jobs waiting for session.zip upload confirmation. 2 concurrent jobs
Failed jobs count toward the daily limit Jobs that fail still consume capacity and count toward the daily rate limit. They do not count toward the concurrent limit once they have completed (successfully or with an error).

When either limit is exceeded, the Create Capture endpoint returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait before retrying.

Get Usage

GET /v1/usage API Token

Returns current API usage and rate limit status for the workspace associated with the API token.

Example Request

curl https://poly.cam/api/v1/usage \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "reconstruct": {
    "used": 6,
    "limit": 25,
    "remaining": 19,
    "windowHours": 24,
    "resetAt": 1706227200000,
    "concurrent": {
      "active": 1,
      "limit": 2,
      "remaining": 1
    }
  }
}

Response Fields

FieldTypeDescription
reconstruct.usednumberNumber of reconstruction jobs started in the current rolling window.
reconstruct.limitnumberMaximum jobs allowed per rolling window.
reconstruct.remainingnumberJobs remaining before hitting the daily limit.
reconstruct.windowHoursnumberRolling window duration in hours.
reconstruct.resetAtnumber?Unix timestamp (ms) when the oldest in-window job expires and a slot opens. Absent when no jobs are in the window.
reconstruct.concurrent.activenumberNumber of currently active reconstruction jobs.
reconstruct.concurrent.limitnumberMaximum concurrent reconstruction jobs allowed.
reconstruct.concurrent.remainingnumberConcurrent slots available.

Error Responses

StatusReason
401Missing or invalid API token

Exports

Export endpoints let you download capture artifacts directly and convert captures to additional mesh and floorplan formats. All export endpoints require API token authentication.

Download Artifacts

GET /v1/captures/:captureId/export API Token

Returns time-limited signed download URLs for all available artifacts of a capture. Each URL is valid for 1 hour (3600 seconds).

Path Parameters

ParameterTypeDescription
captureId string The unique ID of the capture.

Example Request

curl https://poly.cam/api/v1/captures/cap_abc123/export \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "captureId": "cap_abc123",
  "exports": {
    "thumbnail.jpg": {
      "url": "https://storage.example.com/signed/thumbnail.jpg?token=...",
      "expiresIn": 3600
    },
    "skybox.jpg": {
      "url": "https://storage.example.com/signed/skybox.jpg?token=...",
      "expiresIn": 3600
    },
    "original.gltf": {
      "url": "https://storage.example.com/signed/original.gltf?token=...",
      "expiresIn": 3600
    },
    "original_geometry.bin": {
      "url": "https://storage.example.com/signed/original_geometry.bin?token=...",
      "expiresIn": 3600
    },
    "textures/0.jpg": {
      "url": "https://storage.example.com/signed/textures/0.jpg?token=...",
      "expiresIn": 3600
    }
  }
}

The exports object contains one entry per available artifact. Only artifacts that exist for this capture are included. See Artifacts for the full list of possible artifact names.

Error Responses

StatusReason
401Missing or invalid API token
404Capture not found, does not belong to your workspace, or has no exportable artifacts
500Failed to generate export URLs

Start an Export Conversion Job

POST /v1/captures/:captureId/export API Token

Starts an asynchronous conversion job to export a capture to a different format. This supports both mesh formats (e.g. OBJ, FBX, STL) and floorplan formats (e.g. PDF, SVG, DXF, report CSV). The response includes a jobId that you can poll via GET /v1/exports/:jobId to check progress and retrieve the download URL when complete.

Path Parameters

ParameterTypeDescription
captureId string The unique ID of the capture to export.

Request Body

FieldTypeRequiredDescription
format string Yes Target export format. See Export Formats below for the full list of mesh and floorplan formats.
georeference boolean No If true, georeference the output. Only applicable to mesh point cloud formats (PLY, LAS, PTS, XYZ). Ignored for floorplan exports.
pointCloudDensity number No Point density for mesh point cloud formats (PLY, LAS, PTS, XYZ). Ignored for floorplan exports.

Example Request — Mesh export

curl -X POST https://poly.cam/api/v1/captures/cap_abc123/export \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{
    "format": "obj"
  }'

Example Request — Floorplan PDF export

curl -X POST https://poly.cam/api/v1/captures/cap_abc123/export \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{
    "format": "pdf"
  }'

Example Request — Floorplan report CSV export

curl -X POST https://poly.cam/api/v1/captures/cap_abc123/export \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{
    "format": "report-csv"
  }'

Response 202

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "exportFormat": "pdf",
  "captureId": "cap_abc123"
}
Asynchronous processing Export conversion runs in the background. Use the returned jobId with GET /v1/exports/:jobId to poll for completion and retrieve the download URL.
Floorplan exports Floorplan formats (png, svg, pdf, dxf, json, report-csv, report-pdf) require the capture to have floorplan data (i.e. the capture mode must be floorplan). The georeference and pointCloudDensity options are not applicable to floorplan exports.

Error Responses

StatusReason
400Invalid JSON body or invalid/missing format
401Missing or invalid API token
404Capture not found or does not belong to your workspace
422Capture has no mesh available for export (mesh formats) or no floorplan data available (floorplan formats)
500Failed to create export job

Get Export Job Status

GET /v1/exports/:jobId API Token

Returns the current status of an export conversion job. When the job completes successfully, the response includes an outputUrl with a download link for the exported file.

Path Parameters

ParameterTypeDescription
jobId string The job ID returned from POST /v1/captures/:captureId/export.

Example Request

curl https://poly.cam/api/v1/exports/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200 — Pending/Running

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "type": "export",
  "exportFormat": "obj",
  "captureId": "cap_abc123",
  "createdAt": 1706140800000
}

Response 200 — Complete

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "complete",
  "type": "export",
  "exportFormat": "obj",
  "captureId": "cap_abc123",
  "createdAt": 1706140800000,
  "outputUrl": "https://storage.example.com/exports/capture.obj?token=..."
}

Response 200 — Failed

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "type": "export",
  "exportFormat": "obj",
  "captureId": "cap_abc123",
  "createdAt": 1706140800000,
  "error": "Conversion failed: insufficient mesh data"
}
Job types The type field is "export" for mesh conversion jobs and "floorplan-export" for floorplan conversion jobs.

Job Statuses

StatusDescription
pendingJob is queued and waiting to be processed.
runningJob is currently being processed.
completeJob finished successfully. outputUrl is available.
failedJob failed. error field contains the reason.
Polling recommendation Poll this endpoint every few seconds until status is "complete" or "failed". Export jobs typically complete within a minute.

Error Responses

StatusReason
401Missing or invalid API token
404Export job not found or does not belong to your workspace

Capture Object

Every capture returned by the API has the following shape:

FieldTypeDescription
idstringUnique capture identifier.
namestringDisplay name of the capture.
descriptionstring?Optional description.
tagsstring[]User-assigned tags.
createdAtnumberUnix timestamp (ms) when the capture was created.
updatedAtnumberUnix timestamp (ms) of the last update.
visibilitystringVisibility setting (e.g. "private", "public").
sessionobjectSession metadata — see below.
processingobjectProcessing status and timing — see below.
meshobject?Mesh geometry info (if available).
geoDataobject?Geographic data (omitted when location is hidden).
availableArtifactsstring[]List of artifact filenames that exist for this capture.
thumbnailstring?URL to the thumbnail image, if available.
externalIdstring?External reference ID for linking to external systems (max 256 characters). Set via PATCH.

session object

FieldTypeDescription
modestringCapture mode. See Capture Modes.
devicestring?Device used (e.g. "iPhone 15 Pro").
appVersionstring?Polycam app version.
durationnumber?Capture session duration in seconds.
importedboolean?Whether the capture was imported.
droneboolean?Whether the capture was taken with a drone.

processing object

FieldTypeDescription
statusstring"completed" or "pending".
sessionDurationnumber?Session duration in seconds.
processingDurationnumber?Cloud processing time in seconds.
processingVersionstring?Version of the processing pipeline used.
jobobject?Details of the most recent reconstruction/processing job, if one exists — see below.

processing.job object (optional)

Present when the capture has an associated batch job (e.g. after triggering reconstruction). This is the most reliable way to detect when processing has finished from a webhook payload.

FieldTypeDescription
idstringThe job ID (matches the jobId returned by Start Reconstruction).
typestringThe type of processing job.
statusstringJob status, e.g. "pending", "running", "completed", or "failed".
createdAtnumber?Unix timestamp (ms) when the job was created.
finishedAtnumber?Unix timestamp (ms) when the job finished.
errorstring?Failure reason, present when status is "failed".

mesh object (optional)

FieldTypeDescription
vertexCountnumberTotal number of mesh vertices.
faceCountnumberTotal number of mesh faces.
bboxSize[x, y, z]Bounding box dimensions in meters.
bboxCenter[x, y, z]Bounding box center coordinates.

Capture Modes

The session.mode field returns one of the following values:

ModeDescription
lidarLiDAR scan (default for legacy captures).
photoPhotogrammetry capture (photo, object, or objectCapture modes).
floorplanRoomPlan-based floorplan scan.
spaceSpace capture (large-area scanning).
360360° spherical capture.
importImported 3D model or camera upload.
splatGaussian splat capture.
generatedAI-generated 3D model.

Artifacts

The availableArtifacts array lists filenames of data files associated with a capture. These indicate which export formats and data are available.

ArtifactDescription
thumbnail.jpgCapture thumbnail image.
video.mp4Video trailer / flythrough.
edited.gltfEdited glTF mesh (only present if the capture has been edited).
original.gltfOriginal glTF mesh.
edited_geometry.binBinary geometry data for edited.gltf.
original_geometry.binBinary geometry data for original.gltf.
textures/*Texture image files referenced by the glTF meshes (e.g. textures/0.jpg, textures/1.jpg). The exact filenames vary per capture.
skybox.jpgSkybox texture (360° captures).
original_floorplan.jsonOriginal floorplan data (from Apple RoomPlan).
optimized_floorplan.jsonOptimized floorplan data.
edited_floorplan.jsonUser-edited floorplan data.
import.zipOriginal imported file.
edited.splatEdited Gaussian splat (only present if the capture has been edited).
original.splatOriginal unedited Gaussian splat.
splat.plyGaussian splat in PLY format.

Export Formats

The following formats are supported by the export conversion endpoint. Pass one of these values as the format field in the request body.

Mesh Formats

These formats require the capture to have mesh data (3D model).

FormatExtensionDescription
glb.glbGLB — self-contained binary glTF mesh.
obj.objWavefront OBJ — widely supported mesh format.
fbx.fbxAutodesk FBX — common in game engines and 3D tools.
stl.stlSTL — standard for 3D printing.
dae.daeCOLLADA — XML-based interchange format.
usdz.usdzUSDZ — Apple's AR-ready format.
ply.plyPLY — point cloud format. Supports pointCloudDensity and georeference.
las.lasLAS — industry-standard point cloud. Supports pointCloudDensity and georeference.
pts.ptsPTS — point cloud format. Supports pointCloudDensity and georeference.
xyz.xyzXYZ — simple point cloud format. Supports pointCloudDensity and georeference.

Floorplan Formats

These formats require the capture to have floorplan data (i.e. captures made with the floorplan mode). The georeference and pointCloudDensity options are not applicable to floorplan exports.

FormatExtensionDescription
png.pngPNG — rasterized 2D floorplan image.
svg.svgSVG — scalable vector floorplan drawing.
pdf.pdfPDF — printable 2D floorplan document.
dxf.dxfDXF — AutoCAD-compatible floorplan drawing.
json.jsonJSON — structured floorplan data.
report-csv.csvCSV report — tabular room measurements and areas.
report-pdf.pdfPDF report — formatted report with room measurements and areas.

Health Check

GET /v1/health

Simple health check. No authentication required.

curl https://poly.cam/api/v1/health

Response 200

{
  "status": "ok",
  "service": "api-v1"
}
Polycam Enterprise API v1 Documentation