Authentication and access
Requests use the current site’s /api base path. Sign in through the application
to establish a Better Auth session cookie. There are no API keys or bearer tokens. Protected
endpoints require that session, and mutations require an Origin header matching
the application origin. This is a session-based application API, not an unauthenticated
generation service.
GET /api/health and GET /api/config are public.
GET /api/me returns the signed-in user, credits, and payment-processing state,
or null when signed out. Health reports service liveness; it does not verify
generation, payment, email delivery, or photo quality.
Reference and generated photos are private. Resource operations check ownership.
Authentication routes under /api/auth/ are handled by Better Auth.
POST /api/webhooks/polar is a signature-verified payment-provider callback, not
a customer endpoint.
Example requests
Public requests do not need credentials:
curl -i https://aiphotosofyou.com/api/health \
-H 'API-Version: 1'
curl https://aiphotosofyou.com/api/config
After signing in, an authorized browser agent can read the account's photos in the same-origin browser session. Session cookies are credentials: do not share them or include them in logs. This read does not spend credits:
const response = await fetch('/api/photos?limit=10', {
credentials: 'same-origin',
headers: { 'API-Version': '1' }
});
const page = await response.json();
To create a profile with the owner's permission, use the same signed-in browser context. The browser supplies the matching Origin header automatically:
const response = await fetch('/api/subjects', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'API-Version': '1' },
body: JSON.stringify({ name: 'My profile' })
});
Versioning and deprecation
Send API-Version: 1 to select the current major version. Existing clients that
omit this header use version 1. Responses identify the version with
API-Version: 1; unsupported values return HTTP 400 with code
unsupported_api_version. This contract applies to the Studio endpoints listed
below, excluding Better Auth routes and Polar webhooks.
Breaking contract changes require a new major version. Clients should tolerate additive
fields and unknown error codes. Planned retirement will be announced here and signaled with
the Deprecation header (a
Structured Field date, such as @1798761600) and the
Sunset header
(an HTTP date) when dates are scheduled. No version retirement is currently scheduled.
Photo workflow
-
Get or create your saved reference set with
POST /api/subjectsand upload reference photos withPOST /api/uploads. Uploads use multipart fieldssubjectIdandfile; accepted files are JPEG, PNG, or WebP, at most 2 MiB each, with up to five references per account across devices. Repeated subject creation returns the existing set with 200; the first creation returns 201. -
Obtain explicit consent before submitting a shoot. Generation requires
consent: true, reference photos, and sufficient credits. Each requested output reserves two credits. Paid generation must be authorized by the account owner. -
Submit
POST /api/shootsorPOST /api/shoots/batchwith a new UUID. Reuse that UUID when retrying the same submission. A successful response acknowledges the job; it does not mean the photos are ready. -
Poll
GET /api/shootsfor progress andGET /api/photosfor available outputs. A shoot can have partial failures; successful photos remain available and failed outputs receive credit refunds. Output quality and likeness require human review. -
Read image bytes at
GET /api/photos/{id}/file. Add?downloadto request an attachment.
Single shoots accept 1–12 outputs with ratio 2:3, 1:1, or
3:2. Batches accept up to 48 distinct style or curated pack shot IDs and 48
total outputs. Each batch item can include a prompt of up to 2,000 characters; the shared
batch prompt follows those directions for every shot. At most two shoots or batches can be
active per account. Custom generation and editing require a nonblank prompt. Editing and
enhancement use an owned generated photo as sourceId; enhancement does not
promise exact dimensions or pixel preservation.
Endpoints
All paths below are relative to /api. The OpenAPI specification includes
request fields and response descriptions.
| Method and path | Purpose |
|---|---|
GET /healthGET /configGET /me
|
Liveness, public configuration, and current session. |
GET, POST /subjectsDELETE /subjects/{id} |
List, create, or delete profiles. Delete associated shoots before deleting a profile. |
POST /uploads |
Upload a private reference photo. |
GET /photosGET /photos/{id}/filePATCH, DELETE /photos/{id}
|
List photos, retrieve bytes, set a favorite, or delete a photo. |
GET, POST /shootsPOST /shoots/batchDELETE /shoots/{id}
|
List progress, create paid generation/edit/enhancement jobs, create a style batch, or delete a finished shoot and its photos. |
POST /checkoutGET /checkout/{id}POST /billingGET /credits
|
Create a hosted checkout, read payment status, open the billing portal, or read the latest 100 ledger entries. |
POST /attribution |
Save first account attribution with the user’s measurement consent. |
DELETE /account |
Delete the account and media with {"confirmation":"DELETE"}. Requires
no active shoots. Required financial records are retained.
|
Pagination, payments, and failures
Photo and shoot lists return {"items": [...], "nextCursor": ...}. Use
limit (1–100, default 100) and pass the returned nextCursor as the
next request’s cursor. A null cursor marks the final page.
A checkout redirect is not proof of payment. Credit grants depend on verified payment
reconciliation. Check /checkout/{id} and the current balance before proceeding.
Application errors return JSON with an error message and a machine-readable
code, for example
{"error":"Please sign in to continue.","code":"unauthenticated"}. Branch on the
code and HTTP status, rather than parsing the message. The OpenAPI
ApiError schema covers 4xx and 5xx responses. Common statuses are 400 for
invalid input, 401 for missing authentication, 403 for an invalid origin, 404 for missing or
unowned resources, 409 for credit/limit/active-work conflicts, 413 for oversized uploads,
429 for rate limits, and 503 for unavailable integrations. Deleting photos, shoots, or
accounts can be blocked while active jobs need them.
Rate limits
Profile creation allows 120 requests per hour, reference uploads 600, checkout creation 20,
and billing portal creation 30. Each quota is per account. Responses after a quota check
carry RateLimit-Policy (for example "profile";q=120;w=3600) and
RateLimit (for example "profile";r=119;t=3600). The values
describe requests allowed per window, requests remaining, and seconds until reset. These
Structured Fields follow
the HTTPAPI rate-limit draft, which is not a published RFC. Other endpoints do not advertise an account request quota.
HTTP 429 responses include Retry-After in seconds. Wait at least that long
before retrying; successful responses do not guarantee later requests will be accepted.
Requests rejected before a quota check may omit quota headers. Separate profile, upload,
credit, and active-shoot limits can return 409 and are not hourly request quotas.