The engine behind your next app
From an idea
to your API.
Build around owned assets, explicit quotes and durable creation jobs. Use the same workspace as the studio.
Create a developer keyconst response = await fetch('/api/v1/models');
const { data: models } = await response.json();
// Select controls from the model's capabilities.
// Quote first. Submit only after user approval.
// Store the job ID and reuse its idempotency key.Start with a scoped key.
Create a named key in your workspace. Give it only the scopes it needs and set a USD spending limit. The secret appears once. Keep it on your server, never in a public application bundle.
Authorization: Bearer larper_YOUR_SECRET
Content-Type: application/jsonThe base path is /api/v1 on your Stuntmandeployment. Models are public; workspace resources require authentication. The service resolves your workspace from the credential.
A creation has a lifecycle.
Discover a model. Upload and complete references you own. Send its supported settings, prompt, reference IDs and consent to the quote endpoint. Display the price and wait for approval before creating a job.
POST /api/v1/jobs
Idempotency-Key: <a stable UUID>
{ "quoteId": "<approved quote ID>" }
// Response: { data: Job }
// Keep the same key when recovering a lost response.Read the job until it reaches completed, failed or cancelled. Queued, submitting, processing and saving describe progress. Uncertain means provider acceptance needs reconciliation; creating another request could duplicate paid work.
One brand, across your tools.
Save a versioned brand with product facts, audience, voice, visual direction and owned reference assets. Conversations use an exact brand revision. The website, API and MCP read the same records.
GET /api/v1/brands
POST /api/v1/brands
POST /api/v1/brands/:id/revisions
POST /api/v1/designer-conversations
GET /api/v1/designer-conversations/:id
POST /api/v1/designer-conversations/:id/turns
POST /api/v1/designer-turns/:id/quote
// Each POST needs a stable Idempotency-Key.
// Retry the same key and body after a lost response.
// Quotes prepare a price; they never create a media job.Use brands:read, brands:write, designer:read and designer:write only where needed. Preparing a quote also needs jobs:write. Reading that quote in Studio needs jobs:read. Existing generation-only keys cannot read brand records.
The development designer uses a limited Claude allowance, separate from media credits. Text is sent to Claude when you request a reply; image inspection needs explicit image-transfer consent. A prepared response always requires a separate media quote and generation approval. MCP scope availability also depends on the OAuth issuer configuration.
The core endpoints.
/webhooksList or register signed completion notification endpoints./webhooks/:id/deliveriesInspect attempts, responses and delivery outcomes./deliveries/:id/retryRequest another attempt for an undelivered event./modelsDiscover image/video capabilities and supported controls./assets/uploadRequest a private direct-upload URL. PUT bytes, then complete the asset./assets/:id/completeVerify an uploaded object before using it as a reference./quotesReview a fixed, expiring customer price for a creation./jobsSubmit {quoteId} with an Idempotency-Key UUID./jobs/:idRead durable status and completed private assets./jobs/:id/cancelRequest cancellation where supported by the provider./usageInspect the shared workspace balance and settlement ledger.Price before processing.
Amounts are integer USD microdollars: 1 USD equals 1,000,000 units. One credit equals 10,000 microdollars; 100 credits equal US$1 of usage allowance. Quote responses include customerMicros and expiresAt. A job reserves its amount once; the usage ledger records reservation, capture and release.
A changed request needs a new quote and idempotency key. Retry the same submission with its original key if the response was lost. Only retry a failed creation as new paid work after reviewing a new quote.
Failures return an error code, a readable message and a requestId. Keep the request ID for support. Asset URLs are private, expire and must not be treated as permanent storage.
Read the evidence behind a test.
Connect and select your advertiser in Accounts, then use a key with ads:read to request a report. The date range is inclusive and limited to 31 days. This queues reads of your advertiser data; it does not publish ads or change a budget.
POST /api/v1/ad-reports
Idempotency-Key: <a stable UUID>
{ "advertiserId": "<selected advertiser ID>",
"fromDate": "2026-10-01", "toDate": "2026-10-07" }
GET /api/v1/ad-reports/<report ID>
GET /api/v1/ad-snapshots/<snapshot ID>
GET /api/v1/ad-snapshots/<snapshot ID>/facts?limit=50Keep the same request key after a lost response. Queued reports need an available development worker. Preserve returned metric strings, currency, timezone, partial-data labels and attribution caveats. Missing data is not zero, and attributed conversions are not proof of profitable demand.
MCP exposes the same report requests and immutable snapshots through a separate ads:read grant. Registration and consent for that new grant still need verification in your host.
Receive signed completion events.
Register a public HTTPS receiving URL in the studio. Save its signing secret when it appears. Verify signatures on your server, reject invalid or stale requests, and handle repeated event IDs safely. Your endpoint must acknowledge delivery promptly.
import { createHmac, timingSafeEqual } from 'node:crypto';
// Read the request body as raw UTF-8 bytes, before JSON parsing.
const timestamp = request.headers.get('X-Larper-Timestamp') ?? '';
const signature = request.headers.get('X-Larper-Signature') ?? '';
const raw = await request.text();
const expected = 'v1=' + createHmac('sha256', signingSecret)
.update(timestamp + '.' + raw).digest('hex');
const timely = /^\d{1,12}$/.test(timestamp) &&
Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const valid = timely && /^v1=[a-f0-9]{64}$/.test(signature) &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return new Response('Invalid signature', { status: 400 });
const event = JSON.parse(raw);
// Deduplicate event.id durably before applying its side effects.Events use job.completed, job.failed or job.cancelled. X-Larper-Event-Id identifies the event; X-Larper-Delivery-Id identifies its delivery record. The signature covers the timestamp and raw body. A completed event includes its job ID so your server can fetch owned results through the API.
Manage webhooksYour workspace, in conversation.
The MCP and ChatGPT clients use the same application service. External host connection and approval are separate verification steps. This development guide does not claim a published ChatGPT app or a verified host installation.
Manage your keys