Browse the docs Open Close
API Reference
every object, every endpoint
The complete reference, generated from the live API spec. Download it whole as Markdown, OpenAPI or Postman.
Objects
Template
A reusable design with addressable layers.
| Field | Type | Description |
|---|---|---|
uuid |
string | Stable public identifier. |
title |
string | Human-readable name. |
width |
integer | Canvas width in pixels. |
height |
integer | Canvas height in pixels. |
layers |
Layer[] | Addressable layers and their modifications. |
created_at |
datetime | ISO-8601 creation timestamp. |
Layer
A single addressable element inside a template.
| Field | Type | Description |
|---|---|---|
layer |
string | Layer key used when overriding content. |
type |
string | One of image, text, empty, qr_code, barcode. |
available_modifications |
object | Override keys this layer accepts (image_url, text, background_color, qr_content, barcode_content). |
Screenshot
A rendered capture of a web page.
| Field | Type | Description |
|---|---|---|
id |
string | Screenshot UUID. |
url |
string | Captured page URL. |
image_url |
string | URL of the rendered image. |
width |
integer | Viewport width in pixels. |
height |
integer | Viewport height in pixels. |
created_at |
datetime | ISO-8601 creation timestamp. |
Snap
A render job produced from a template or collection.
| Field | Type | Description |
|---|---|---|
id |
string | Snap UUID. |
media_url |
string|null | URL of the finished asset, null until completed. |
type |
string | image or pdf. |
status |
string | pending, processing, completed, failed or canceled. |
is_async |
boolean | Whether the snap was queued. |
webhook_url |
string|null | Callback URL for async completion. |
completed_at |
datetime|null | When rendering finished. |
last_error |
string|null | Failure reason, when status is failed. |
variants |
array|null | Per-layer content overrides applied. |
template_id |
string|null | Source template UUID. Null for a snap rendered from an inline design, which has no template. |
template_title |
string|null | Source template name. Null for a snap rendered from an inline design. |
created_at |
datetime | ISO-8601 creation timestamp. |
Endpoints
Authentication
/v1/auth
Verify the current token
Returns the account the token belongs to, with the company it belongs to. Use it to confirm a token is valid and to read the signed-in identity. `company` is null when the account belongs to no company.
Example request
curl
curl https://snapsbrew.com/api/v1/auth \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": {
"email": "[email protected]",
"name": "Ada Lovelace",
"company": "Analytical Engines"
}
}
{
"message": "Unauthenticated."
}
{
"message": "Invalid ability provided."
}
Usage
/v1/usage
Get current usage
Returns purchased, used and remaining requests for the active billing period.
The response carries an `X-Request-Uuid` header you can quote in support tickets.
Example request
curl
curl https://snapsbrew.com/api/v1/usage \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"period_start_date": "2026-05-01T00:00:00Z",
"period_end_date": "2026-05-31T23:59:59Z",
"period_purchased_requests": 1000,
"period_used_requests": 240,
"period_available_requests": 760
}
{
"message": "Unauthenticated."
}
Guides
/v1/guides/design
Get the design guide
Returns design guidance for building a template: canvas presets, the back-to-front layer order, typography and color rules, composition recipes, anti-patterns, and a preflight checklist. Read it before you write a `layers` array.
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/design \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": {
"typography": {
"scale": "On a 1080px canvas: headline 64\u2013120px, subhead 36\u201356px, body 24\u201336px."
}
}
}
{
"message": "Unauthenticated."
}
/v1/guides/layers
Get the layer schema
Returns the design-time layer vocabulary: the layer object shape, every property name, the value shape of each property, and a worked example. Use it to build the `layers` array a template write accepts.
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/layers \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": {
"property_names": [
"textContent",
"backgroundType",
"fontFamily"
]
}
}
{
"message": "Unauthenticated."
}
/v1/guides/variant-properties
List variant properties
Returns every property you can apply to a layer in `variants`, with its value type, the layer types it applies to, and an example. Pair it with the `available_modifications` of a template layer.
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/variant-properties \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": [
{
"property": "text",
"value_type": "string",
"applies_to": "text layers",
"description": "Replace a text layer's content.",
"example": {
"layer": "headline",
"text": "Summer Sale \u2014 40% off"
}
}
]
}
{
"message": "Unauthenticated."
}
/v1/guides/editor-document
Get the editor document contract
Returns the versioned v2 editor-document contract: the resource it describes, property constraints, persistence rules, conflict recovery, and a safe workflow. Read it before you change an editor document.
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/editor-document \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": {
"contract_version": 2,
"editor_document_version": 2,
"resource": "template.editor_document"
}
}
{
"message": "Unauthenticated."
}
/v1/guides/platforms
List platform guides
Returns one line per platform: the size preset to start from, its dimensions and why that preset is the default. Read it to choose a platform, then read that platform for the full rules. The response also carries `specs_reviewed`, the month the published specs were last checked.
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/platforms \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": [
{
"platform": "instagram",
"name": "Instagram",
"start_here": "instagram_post",
"dimensions": "1080x1350",
"why": "A 4:5 portrait post fills the most vertical space the feed will give a single image."
}
]
}
{
"message": "Unauthenticated."
}
/v1/guides/platforms/{platform}
Get one platform guide
Returns everything one platform does to an image: how it crops the frame, the directional UI safe zones where it paints its own controls, the accepted formats and size limits, the export width and color profile, its size presets, and its rules and anti-patterns.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
platform |
path | string | Yes | A key from the index route, or a common alias. Maximum 64 characters. Valid keys: instagram, facebook, x, linkedin, threads, pinterest, tiktok, youtube, snapchat, whatsapp, web, ads, print. |
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
An alias is accepted. `/v1/guides/platforms/insta` returns the same guide as `/v1/guides/platforms/instagram`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/platforms/instagram \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": {
"platform": "instagram",
"name": "Instagram",
"specs_reviewed": "2026-09",
"start_here": "instagram_post"
}
}
{
"message": "Unauthenticated."
}
{
"message": "No platform guide matches `myspace`.",
"available": [
"instagram",
"facebook",
"x",
"linkedin",
"threads",
"pinterest",
"tiktok",
"youtube",
"snapchat",
"whatsapp",
"web",
"ads",
"print"
]
}
{
"message": "The platform field must not be greater than 64 characters."
}
/v1/guides/purposes
List image purpose guides
Returns one line per purpose: what that kind of image is, when to use it and the size presets that fit it. Read it to choose a purpose, then read that purpose for the full rules.
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/purposes \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": [
{
"purpose": "link_preview",
"name": "Link preview / meta tag image",
"use_when": "You are shipping a page and you want the shared link to look deliberate.",
"sizes": [
"open_graph",
"twitter_card"
]
}
]
}
{
"message": "Unauthenticated."
}
/v1/guides/purposes/{purpose}
Get one image purpose guide
Returns the job one kind of image has to do: the size presets that fit it, the rules and failure modes specific to it, a layer recipe in the Snapsbrew vocabulary, an anti-pattern list and a preflight checklist. The `link_preview` purpose also returns the og: and twitter: meta tags to put in the page head.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
purpose |
path | string | Yes | A key from the index route, or a common alias. Maximum 64 characters. Valid keys: link_preview, feed_post, carousel, story, profile_picture, cover, video_thumbnail, ad, product_shot, email_header, article_header, presentation_slide. |
The catalog is the same for every account. Cache the response and refresh it on a new API version.
A guide call costs no credits and does not appear in `GET /v1/usage`.
An alias is accepted. `/v1/guides/purposes/og` returns the same guide as `/v1/guides/purposes/link_preview`.
Example request
curl
curl https://snapsbrew.com/api/v1/guides/purposes/link_preview \
-H 'Authorization: Bearer <your-token>' \
-H 'Accept: application/json'
Example responses
{
"data": {
"purpose": "link_preview",
"name": "Link preview / meta tag image",
"layer_recipe": "base fill -> optional photo with a dark scrim -> headline -> supporting line -> logo."
}
}
{
"message": "Unauthenticated."
}
{
"message": "No purpose guide matches `wallpaper`.",
"available": [
"link_preview",
"feed_post",
"carousel",
"story",
"profile_picture",
"cover",
"video_thumbnail",
"ad",
"product_shot",
"email_header",
"article_header",
"presentation_slide"
]
}
{
"message": "The purpose field must not be greater than 64 characters."
}
Templates
/v1/templates
List templates
Returns a cursor-paginated list of your templates.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor |
query | string | No | Cursor token from a previous response's meta.next_cursor. |
Example request
curl
curl https://snapsbrew.com/api/v1/templates \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": [
{
"uuid": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"title": "Template 1",
"width": 1200,
"height": 630,
"layers": [
{
"layer": "review_title",
"type": "text",
"available_modifications": {
"text": "The worst Time-Travel movie ever made"
}
}
],
"created_at": "2026-05-01T12:00:00Z"
}
],
"meta": {
"previous_cursor": null,
"next_cursor": "eyJpZCI6MTB9"
}
}
/v1/templates/sizes
List canvas size presets
Returns every canvas size preset a template write accepts as `size` — Instagram, Facebook, X, LinkedIn, Threads, Pinterest, TikTok, YouTube, Snapchat, WhatsApp, web and email, display ads, and print. Each preset gives its dimensions, aspect ratio, and the safe margin to keep content away from the edges.
Example request
curl
curl https://snapsbrew.com/api/v1/templates/sizes \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": [
{
"size": "instagram_story",
"name": "Instagram Story (1080x1920)",
"platform": "Instagram",
"width": 1080,
"height": 1920,
"aspect_ratio": "9:16",
"safe_margin_px": 97
},
{
"size": "open_graph",
"name": "Open Graph / Link Preview (1200x630)",
"platform": "Web & email",
"width": 1200,
"height": 630,
"aspect_ratio": "1.9:1",
"safe_margin_px": 47
}
]
}
/v1/templates/{templateUuid}
Get a template
Returns a single template, its parsed layers and its publish state. The layers are the draft, so read `has_unpublished_edits` to know whether the draft is what renders.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template. |
BREAKING CHANGE. The template fields moved under a `data` key. Read `data.uuid`, not `uuid`. Every success body of this API is `{"data": ...}`, a list adds `meta` and a write adds `message`.
`size` is the canvas size preset the template is stored under, the same value POST /v1/templates returns. It reads `custom` when the dimensions match no preset. GET /v1/templates/sizes lists every preset.
`published_version` is the version number snaps and previews render. It is null until you publish the template.
`has_unpublished_edits` is true when the draft is not the same as the published version. A layer edit, a resize and an editor-document edit all set it. Call POST /v1/templates/{templateUuid}/publish to make the draft render.
Example request
curl
curl https://snapsbrew.com/api/v1/templates/<uuid> \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": {
"uuid": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"title": "Template 1",
"size": "open_graph",
"width": 1200,
"height": 630,
"layers": [
{
"layer": "review_poster",
"type": "image",
"available_modifications": {
"image_url": "https://snapsbrew.com/crazy-doc-brown-jailed"
}
},
{
"layer": "review_title",
"type": "text",
"available_modifications": {
"text": "The worst Time-Travel movie ever made"
}
}
],
"published_version": 2,
"has_unpublished_edits": true,
"created_at": "2026-05-01T12:00:00Z"
}
}
{
"message": "Not found."
}
/v1/templates
Create a template
Creates a template and generates its preview image. Requires the create-template policy.
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
title |
string | Yes | required|string|max:255 |
Template name. |
size |
string | No | nullable|in:<preset> |
Canvas size preset, for example "instagram_story" or "youtube_thumbnail". It sets width and height for you. GET /v1/templates/sizes lists every preset. |
width |
integer | No | required_without:size|numeric|min:1|max:4000 |
Canvas width in pixels. Required when you send no size preset. Ignored when you send a size preset. |
height |
integer | No | required_without:size|numeric|min:1|max:4000 |
Canvas height in pixels. Required when you send no size preset. Ignored when you send a size preset. |
layers |
array | No | nullable|array |
Layer definitions; stored JSON-encoded. An absent or null list creates an empty canvas. |
Send a `size` preset or an explicit `width`/`height`. A template saved with dimensions that match a preset is stored under that preset.
A body with no `size` and no `width`/`height` answers 422. A create has no stored canvas to fall back on.
The response holds the six fields above and nothing more. Read GET /v1/templates/{templateUuid} for the parsed layers and the publish state.
Example request
curl
curl -X POST https://snapsbrew.com/api/v1/templates \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"title":"Launch card","size":"open_graph","layers":[]}'
Example responses
{
"data": {
"uuid": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"title": "Launch card",
"size": "open_graph",
"width": 1200,
"height": 630,
"created_at": "2026-05-01T12:00:00Z"
},
"message": "Template created successfully"
}
{
"message": "Send a `size` preset or an explicit `width` and `height`.",
"errors": {
"width": [
"Send a `size` preset or an explicit `width` and `height`."
],
"height": [
"Send a `size` preset or an explicit `width` and `height`."
]
}
}
/v1/templates/{templateIdentifier}
Update a template
Updates a template and regenerates its preview image. The path segment accepts the template UUID or the numeric template ID. GET /v1/templates returns the UUID.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateIdentifier |
path | string | Yes | Template UUID or numeric template ID, scoped to your company. A value of only digits reads the numeric ID. Every other value reads the UUID. |
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
title |
string | Yes | required|string|max:255 |
Template name. |
size |
string | No | nullable|in:<preset> |
Canvas size preset, for example "instagram_story" or "youtube_thumbnail". It sets width and height for you. GET /v1/templates/sizes lists every preset. |
width |
integer | No | nullable|numeric|min:1|max:4000 |
Canvas width in pixels. Ignored when you send a size preset. |
height |
integer | No | nullable|numeric|min:1|max:4000 |
Canvas height in pixels. Ignored when you send a size preset. |
layers |
array | No | nullable|array |
Layer definitions; stored JSON-encoded. |
`data` holds the same six fields POST /v1/templates returns. It is a new key, so a client that reads `message` still works.
Read `data.size`, `data.width` and `data.height` after the write. A body that sends `size` sets the dimensions, and a body that sends the dimensions sets `size`.
Example request
curl
curl -X PATCH https://snapsbrew.com/api/v1/templates/<uuid> \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"title":"Updated title"}'
Example responses
{
"data": {
"uuid": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"title": "Updated title",
"size": "open_graph",
"width": 1200,
"height": 630,
"created_at": "2026-05-01T12:00:00Z"
},
"message": "Template updated successfully"
}
{
"message": "Not found."
}
/v1/templates/{templateUuid}/publish
Publish a template
Publishes the current draft of a template as a new immutable version and returns that version number. Snaps and previews render the published version, so an edit takes effect only after this call. The call is safe to repeat: a draft with no new edits returns the version which is already live and creates no second version.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template whose draft you want to publish. |
BREAKING CHANGE. The response fields moved under a `data` key. Read `data.published_version`, not `published_version`.
`data.id` is deprecated. It repeats the value of `data.uuid`, which is the name every other template field uses. Read `data.uuid`. `data.id` stays until a major version removes it.
Example request
curl
curl -X POST https://snapsbrew.com/api/v1/templates/<uuid>/publish \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": {
"id": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"uuid": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"published_version": 2
},
"message": "Template published. New snaps and previews now render this version."
}
{
"message": "Not found."
}
/v1/templates/{templateIdentifier}
Delete a template
Deletes a template. The path segment accepts the template UUID or the numeric template ID. GET /v1/templates returns the UUID. Requires the delete-template policy.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateIdentifier |
path | string | Yes | Template UUID or numeric template ID, scoped to your company. A value of only digits reads the numeric ID. Every other value reads the UUID. |
This body carries no `data` key. Every other success body of this API holds one, and a delete is the exemption: the template is gone, so there is no resource to return.
Example request
curl
curl -X DELETE https://snapsbrew.com/api/v1/templates/<uuid> \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"message": "Template deleted successfully"
}
{
"message": "Not found."
}
Editor documents
/v1/templates/{templateUuid}/document
Get the editor document
Returns the stored v2 editor document and its revision. A template with no draft returns a null document and revision 0. Read this before every write.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template. |
The read gate and the write gate name the same people. Every account in your company that can write the document can also read it.
Example request
curl
curl https://snapsbrew.com/api/v1/templates/<uuid>/document \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"document": {
"version": 2,
"layers": [
{
"key": "headline",
"type": "text",
"properties": [
{
"name": "fontWeight",
"value": 700
}
]
}
],
"groups": []
},
"revision": 4
}
{
"message": "This action is unauthorized."
}
{
"message": "Not found."
}
/v1/templates/{templateUuid}/document
Apply editor operations
Applies a closed list of layer operations and saves the result under the next revision. `set_layer_property` takes `layer_key`, `property` and `value`. `set_layer_type` takes `layer_key` and `type`. Every operation in one call applies together, or none of them applies.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template. |
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
revision |
integer | Yes | required|integer|min:0 |
The revision the last read returned. |
operations |
array | Yes | required|array|max:100 |
One to 100 operation objects, applied in order. |
A rejected operation returns a stable `code`, for example `editor_operation_layer_not_found` or `editor_operation_invalid_value`. Correct the operation and call again.
A malformed body returns the standard validation shape instead, with a message under `errors.revision` or `errors.operations`.
The route allows 30 calls a minute for one token and 180 a minute for one company.
Example request
curl
curl -X PATCH https://snapsbrew.com/api/v1/templates/<uuid>/document \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"revision":4,"operations":[{"operation":"set_layer_property","layer_key":"headline","property":"fontWeight","value":700}]}'
Example responses
{
"document": {
"version": 2,
"layers": [
{
"key": "headline",
"type": "text",
"properties": [
{
"name": "fontWeight",
"value": 700
}
]
}
],
"groups": []
},
"revision": 5
}
{
"code": "editor_document_conflict",
"current_revision": 5,
"current_document": {
"version": 2,
"layers": [],
"groups": []
}
}
{
"code": "editor_operation_layer_not_found"
}
/v1/templates/{templateUuid}/document
Replace the editor document
Replaces the whole v2 editor document and saves it under the next revision. Send the document you read, with your change applied to it. Any layer or property you leave out is gone. Prefer `PATCH` unless you rebuild the document.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template. |
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
revision |
integer | Yes | required|integer|min:0 |
The revision the last read returned. |
document |
object | Yes | required|array |
The complete v2 document, with `layers` and `groups`. |
The document is bounded: 262144 bytes, 1000 nodes, depth 12, and 16384 characters for one string. A document over a limit returns `editor_document_too_large` or `editor_document_budget_exceeded` under `errors.document`.
The route allows 30 calls a minute for one token and 180 a minute for one company.
Example request
curl
curl -X PUT https://snapsbrew.com/api/v1/templates/<uuid>/document \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"revision":4,"document":{"version":2,"layers":[],"groups":[]}}'
Example responses
{
"document": {
"version": 2,
"layers": [],
"groups": []
},
"revision": 5
}
{
"code": "editor_document_conflict",
"current_revision": 5,
"current_document": {
"version": 2,
"layers": [],
"groups": []
}
}
{
"message": "The given data was invalid.",
"errors": {
"document": [
"editor_document_too_large"
]
}
}
/v1/templates/{templateUuid}/document/preflight
Inspect the editor document
Reads the stored document and returns the known draft problems it carries, each with a code, a JSON pointer and a message. An empty `issues` list means the known checks pass. It does not guarantee that the render looks right.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template. |
Run it after the last write and before you publish the template.
Example request
curl
curl https://snapsbrew.com/api/v1/templates/<uuid>/document/preflight \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"issues": [
{
"code": "image_asset_missing",
"path": "/layers/0/source",
"message": "Image layers require a pinned asset id."
}
],
"revision": 4
}
{
"message": "This action is unauthorized."
}
{
"message": "Not found."
}
Media
/v1/media
List media
Returns your uploaded images, 12 per page, newest first.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | No | Page number for the standard paginator. |
Example request
curl
curl https://snapsbrew.com/api/v1/media \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": [
{
"id": 12,
"file_name": "logo.png",
"src": "https://snapsbrew.com/storage/12/conversions/logo-image.jpg"
}
],
"links": {
"first": "...",
"last": "...",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"per_page": 12,
"total": 1
}
}
/v1/media
Upload media
Uploads an image into your media library. Send as multipart/form-data.
Request body
Content-Type: multipart/form-data
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
file |
file | Yes | required, file, image |
The image to upload. |
Example request
curl
curl -X POST https://snapsbrew.com/api/v1/media \
-H 'Authorization: Bearer <your-token>' \
-F '[email protected]'
Example responses
{
"id": 12,
"file_name": "logo.png",
"src": "https://snapsbrew.com/storage/12/conversions/logo-image.jpg"
}
{
"message": "The file must be an image."
}
Screenshots
/v1/screenshots
List screenshots
Returns a cursor-paginated list of your screenshots.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor |
query | string | No | Cursor token from meta.next_cursor. |
Example request
curl
curl https://snapsbrew.com/api/v1/screenshots \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": [
{
"uuid": "a1b2c3",
"url": "https://example.com",
"image_url": "https://snapsbrew.com/storage/screenshots/a1b2c3.png",
"width": 1330,
"height": 780,
"created_at": "2026-05-01T12:00:00Z"
}
],
"meta": {
"previous_cursor": null,
"next_cursor": null
}
}
/v1/screenshots/{screenshotUuid}
Get a screenshot
Returns a single screenshot.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
screenshotUuid |
path | string | Yes | UUID of the screenshot. |
Example request
curl
curl https://snapsbrew.com/api/v1/screenshots/<uuid> \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"data": {
"id": "a1b2c3",
"url": "https://example.com",
"image_url": "https://snapsbrew.com/storage/screenshots/a1b2c3.png",
"width": 1330,
"height": 780,
"created_at": "2026-05-01T12:00:00Z"
}
}
{
"message": "Not found."
}
/v1/screenshots
Create a screenshot
Captures a web page. Defaults to a 1330x780 viewport when width/height are omitted.
Credits — Requires available credits (canCreateSnap); returns 417 when exhausted.
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
url |
string | No | url |
Page to capture. Validated as a URL when present. |
width |
integer | No | integer |
Viewport width. Defaults to 1330. |
height |
integer | No | integer |
Viewport height. Defaults to 780. |
Example request
curl
curl -X POST https://snapsbrew.com/api/v1/screenshots \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com","width":1330,"height":780}'
Example responses
{
"data": {
"id": "a1b2c3",
"url": "https://example.com",
"image_url": "https://snapsbrew.com/storage/screenshots/a1b2c3.png",
"width": 1330,
"height": 780,
"created_at": "2026-05-01T12:00:00Z"
}
}
{
"message": "Unauthenticated."
}
{
"message": "You have reached your snap quota."
}
Snaps
/v1/snaps
Create a snap
Renders a snap from a single template (`template_id`) or every template in a collection (`collection_id`). Provide exactly one of the two.
Synchronous (default): the response carries the finished `media_url` and returns 201.
Asynchronous (`async: true`): the snap is queued, the response returns 202 immediately, and the result is delivered to `webhook_url` on completion.
The API always renders the template's **published** version. Unpublished draft edits are not visible to the API until the template is published.
Credits — Synchronous: 1 credit per rendered template. Asynchronous: 0 credits at accept time (HTTP 202); the credit is charged when the queued snap reaches completed. A repeated `idempotency_key` returns the first snap and charges 0 credits.
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
template_id |
string | No | required_without:collection_id, prohibits:collection_id, exists templates.uuid scoped to company |
UUID of a single template to render. |
collection_id |
string | No | required_without:template_id, prohibits:template_id, exists template_collections.uuid scoped to company |
UUID of a collection; one snap is rendered per template. |
type |
string | No | nullable, in:image,pdf |
Output type. Defaults to image. |
format |
string | No | sometimes, in:png,jpeg,webp |
Image output format (image type only). Defaults to png. |
transparent |
boolean | No | sometimes, boolean; rejected with png-only/webp-only — 422 when combined with format=jpeg |
Render on a transparent background. Valid for png and webp only; jpeg + transparent returns 422. |
async |
boolean | No | sometimes, boolean |
Queue the render and return 202. |
webhook_url |
string | No | url, required_if:async,true, must be a public http or https address |
Callback URL invoked when an async snap completes. The address must be public. A host that resolves to a private, loopback, link-local or reserved range is rejected with 422. |
variants |
array | No | no server validation |
Per-layer content overrides applied to the render. |
idempotency_key |
string | No | sometimes, nullable, string, max:191, letters/digits/underscore/hyphen only, prohibits:collection_id |
Send the same key when you retry after a timeout. The retry returns the same snap and charges no second credit. Keys are honored for 24 hours and are scoped to your account. Not supported with collection_id. |
Every response carries an `X-Request-Uuid` header. Quote it in support tickets.
A collection request returns an array of snap objects, one per template.
A response to a repeated `idempotency_key` carries an `X-Idempotent-Replay: true` header and charges 0 credits.
`webhook_url` must be a public address. A host that resolves to a private, loopback, link-local or reserved range is rejected with 422.
An `idempotency_key` is honored for 24 hours. After that window a retry is a new request and charges a new credit.
Example request
curl
curl -X POST https://snapsbrew.com/api/v1/snaps \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"template_id":"<uuid>","type":"image","variants":[{"layer":"review_title","text":"Hello world"}]}'
async curl
curl -X POST https://snapsbrew.com/api/v1/snaps \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"template_id":"<uuid>","async":true,"webhook_url":"https://example.com/hooks/snap"}'
Example responses
{
"id": "9f1c2d3e",
"media_url": "https://snapsbrew.com/storage/snaps/9f1c2d3e.png",
"type": "image",
"status": "completed",
"is_async": false,
"webhook_url": null,
"completed_at": "2026-05-01T12:00:03Z",
"last_error": null,
"variants": [],
"template_id": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"template_title": "Launch card",
"created_at": "2026-05-01T12:00:00Z"
}
{
"id": "9f1c2d3e",
"media_url": null,
"type": "image",
"status": "pending",
"is_async": true,
"webhook_url": "https://example.com/hooks/snap",
"completed_at": null,
"last_error": null,
"variants": [],
"template_id": "141e8454-8aa1-420a-bd0b-18a85ba949a2",
"template_title": "Launch card",
"created_at": "2026-05-01T12:00:00Z"
}
{
"message": "Not found."
}
{
"message": "You have reached your snap quota."
}
{
"message": "A request with this idempotency_key is already in progress. Retry in a moment. Nothing was created and nothing was charged."
}
{
"message": "The template id field is required when collection id is not present."
}
Snap batches
/v1/snaps/batches
Create a batch of snaps
Renders up to 100 snaps from one template. Every item carries its own `variants` and an optional `identifier` you use to recognize it in the result.
The response is always 202. Nothing is charged at accept time. Each item costs 1 credit when it renders.
`type`, `format` and `transparent` apply to every item in the batch. An item that needs a different format belongs in a different batch.
The API always renders the template's **published** version.
Credits — 0 credits at accept time (HTTP 202). Each item is charged 1 credit when it reaches completed. An item that fails costs nothing. A repeated `idempotency_key` returns the first batch and starts no second one.
Request body
Content-Type: application/json
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
template_id |
string | Yes | required, exists templates.uuid scoped to company |
UUID of the template every item renders from. |
items |
array | Yes | required, array, min:1, max:100 |
One entry per snap. Over 100 entries returns 422 and creates nothing. |
items[].variants |
array | No | each image_url must be a public http or https address |
Per-layer content overrides for this item. |
items[].identifier |
string | No | sometimes, nullable, string, max:191 |
Your own name for this item. Defaults to item_1, item_2, and so on. |
title |
string | No | sometimes, string, max:255 |
A name for the batch, shown in the account. Defaults to "API batch". |
webhook_url |
string | No | nullable, url, max:2048, must be a public http or https address |
ONE request is posted to this address when the batch finishes. A host that resolves to a private, loopback, link-local or reserved range is rejected with 422. |
type |
string | No | sometimes, in:image,pdf |
Output type for every item. Defaults to image. |
format |
string | No | sometimes, in:png,jpeg,webp |
Image output format for every item. Defaults to png. |
transparent |
boolean | No | sometimes, boolean; 422 when combined with format=jpeg |
Render every item on a transparent background. Valid for png and webp only. |
idempotency_key |
string | No | sometimes, nullable, string, max:191, letters/digits/underscore/hyphen only |
Send the same key when you retry after a timeout. The retry returns the same batch and starts no second one. Keys are honored for 24 hours and are scoped to your account. A key already used by POST /v1/snaps returns 409. |
The whole batch is checked against the credit balance before any item is created. A batch of 100 against 40 credits creates nothing, not 40 items.
The balance check is not a reservation. Two batches accepted a second apart read the same balance, so a caller can queue more items than the balance covers. Each item is still charged only when it renders.
Over 100 items returns 422 on `errors.items` and creates nothing. The request is never truncated in silence.
Exactly one webhook is posted, when the batch finishes. There is no per-item webhook on this endpoint.
`webhook_url` must be a public address. A host that resolves to a private, loopback, link-local or reserved range is rejected with 422.
A response to a repeated `idempotency_key` carries an `X-Idempotent-Replay: true` header.
Every response carries an `X-Request-Uuid` header. Quote it in support tickets.
A batch holds one of five statuses. `pending` and `processing` mean it is still running. The three terminal statuses are `completed`, `failed` and `canceled`, and a batch never leaves one. `completed` means the batch ran to the end and at least one item rendered, so read the `failed` count as well as the status. `failed` means zero items rendered. At every terminal status `completed + failed + canceled` equals `total_rows`.
Example request
curl
curl -X POST https://snapsbrew.com/api/v1/snaps/batches \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"template_id":"<uuid>","title":"October launch","items":[{"identifier":"row-1","variants":[{"layer":"headline","text":"Hello"}]},{"identifier":"row-2","variants":[{"layer":"headline","text":"World"}]}]}'
Example responses
{
"uuid": "7c1f2a9e-0b44-4a1e-9f2b-5d3c8e1a7b60",
"title": "October launch",
"status": "processing",
"total_rows": 3,
"processed_rows": 0,
"completed": 0,
"pending": 3,
"failed": 0,
"canceled": 0,
"credits_charged": 0,
"status_url": "https://snapsbrew.com/api/v1/snaps/batches/7c1f2a9e-0b44-4a1e-9f2b-5d3c8e1a7b60"
}
{
"message": "This idempotency_key already belongs to a single snap. Use a new key for a batch."
}
{
"message": "You have reached your snap quota."
}
{
"message": "A batch accepts at most 100 items. Split the request into smaller batches."
}
/v1/snaps/batches/{batchUuid}
Read a batch
Returns the counts, the running credit total, and every item with its status and image URL. Poll this about every 5 seconds and stop when `status` is `completed`, `failed` or `canceled`.
This endpoint does not require an active subscription, so a customer whose plan lapsed can still collect work they already paid for.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
batchUuid |
path | string | Yes | The batch id returned by POST /v1/snaps/batches. |
A batch holds one of five statuses. `pending` and `processing` mean it is still running. The three terminal statuses are `completed`, `failed` and `canceled`, and a batch never leaves one. `completed` means the batch ran to the end and at least one item rendered, so read the `failed` count as well as the status. `failed` means zero items rendered. At every terminal status `completed + failed + canceled` equals `total_rows`.
`credits_charged` is read from the billing ledger, not counted from `completed`. It rises between polls.
`media_url` is set only on an item with status `completed`.
`last_error` on a failed item carries the render error as the renderer reported it. Treat it as a message for a person, never as a stable code to branch on.
A batch id that belongs to another account returns 404, the same as an id that does not exist.
Example request
curl
curl 'https://snapsbrew.com/api/v1/snaps/batches/<batch-uuid>' \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"uuid": "7c1f2a9e-0b44-4a1e-9f2b-5d3c8e1a7b60",
"title": "October launch",
"status": "completed",
"total_rows": 2,
"processed_rows": 2,
"completed": 1,
"pending": 0,
"failed": 1,
"canceled": 0,
"credits_charged": 1,
"status_url": "https://snapsbrew.com/api/v1/snaps/batches/7c1f2a9e-0b44-4a1e-9f2b-5d3c8e1a7b60",
"items": [
{
"id": "9f1c2d3e",
"identifier": "row-1",
"status": "completed",
"media_url": "https://snapsbrew.com/storage/snaps/9f1c2d3e.png",
"last_error": null
},
{
"id": "4b8a7c6d",
"identifier": "row-2",
"status": "failed",
"media_url": null,
"last_error": "Render failed."
}
]
}
{
"message": "No batch with that id exists in your account."
}
/v1/snaps/batches/{batchUuid}/cancel
Cancel a batch
Stops a batch that is still running.
An item that already rendered keeps its image and stays billed. An item that had not started is marked `canceled` and costs nothing. An item that is rendering at that moment can still finish and can still be charged.
This cannot be undone. A canceled batch cannot be resumed. Start a new batch for the items you still want.
Like the read endpoint, this does not require an active subscription: stopping work only lowers the bill.
Credits — Nothing is charged by this call. Items that already rendered keep the credit they cost. Items that never started cost nothing.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
batchUuid |
path | string | Yes | The batch id returned by POST /v1/snaps/batches. |
Canceling twice is safe. A batch that already holds a terminal status is returned unchanged.
One webhook is posted, carrying `event: snap-import.canceled`.
An item that is rendering when the cancel lands can still complete and can still be charged. The counts in the response are the truth at that moment.
A batch id that belongs to another account returns 404, the same as an id that does not exist.
Example request
curl
curl -X POST 'https://snapsbrew.com/api/v1/snaps/batches/<batch-uuid>/cancel' \
-H 'Authorization: Bearer <your-token>'
Example responses
{
"uuid": "7c1f2a9e-0b44-4a1e-9f2b-5d3c8e1a7b60",
"title": "October launch",
"status": "canceled",
"total_rows": 3,
"processed_rows": 3,
"completed": 1,
"pending": 0,
"failed": 0,
"canceled": 2,
"credits_charged": 1,
"status_url": "https://snapsbrew.com/api/v1/snaps/batches/7c1f2a9e-0b44-4a1e-9f2b-5d3c8e1a7b60",
"items": [
{
"id": "9f1c2d3e",
"identifier": "row-1",
"status": "completed",
"media_url": "https://snapsbrew.com/storage/snaps/9f1c2d3e.png",
"last_error": null
},
{
"id": "4b8a7c6d",
"identifier": "row-2",
"status": "canceled",
"media_url": null,
"last_error": null
},
{
"id": "2e5f9a1b",
"identifier": "row-3",
"status": "canceled",
"media_url": null,
"last_error": null
}
]
}
{
"message": "No batch with that id exists in your account."
}
Preview
/v1/templates/{templateUuid}/preview
Preview a template
Renders a free, watermarked preview of a template using its default layer content, or with `variants` applied. Returns a self-contained HTML document (text/html).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateUuid |
path | string | Yes | UUID of the template to preview. |
variants |
query | array | No | Per-layer overrides, e.g. variants[0][layer]=headline&variants[0][text]=Hello. Each item targets a layer key with one of text, image_url (public URL), background_color, qr_content, barcode_content, or hidden. |
The preview is watermarked HTML, not a usable final asset.
No credit is charged and no snap is stored.
Example request
curl
curl 'https://snapsbrew.com/api/v1/templates/<uuid>/preview' \
-H 'Authorization: Bearer <your-token>' \
--output preview.html
Example responses
"<!DOCTYPE html>\u2026watermarked preview\u2026</html>"
{
"message": "Not found."
}
{
"message": "The variants.0.image_url must be a public http or https URL."
}
{
"message": "Too Many Attempts."
}
/v1/snaps/preview
Preview a snap
Renders a free, watermarked preview of the snap you intend to create — a template (`template_id`) plus `variants`. Returns a self-contained HTML document (text/html). Pass the same template_id and variants to POST /v1/snaps to create the final billed snap.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
template_id |
query | string | Yes | UUID of the template to render. |
variants |
query | array | No | Per-layer overrides, e.g. variants[0][layer]=headline&variants[0][text]=Hello. Each item targets a layer key with one of text, image_url (public URL), background_color, qr_content, barcode_content, or hidden. |
The preview is watermarked HTML, not a usable final asset.
No credit is charged and no snap is stored.
Example request
curl
curl 'https://snapsbrew.com/api/v1/snaps/preview?template_id=<uuid>&variants[0][layer]=headline&variants[0][text]=Hello' \
-H 'Authorization: Bearer <your-token>' \
--output preview.html
Example responses
"<!DOCTYPE html>\u2026watermarked preview\u2026</html>"
{
"message": "Not found."
}
{
"message": "The template id field is required."
}
{
"message": "Too Many Attempts."
}
Tags
Embedded Forms
/v1/embedded-forms/{formUuid}
Submit an embedded form
Renders a snap from a configured form. Template forms return one snap; collection forms return an array of snaps.
Layer keys: template forms use the plain layer key (`headline`); collection forms use `{templateUuid}__{layerKey}`. Only layers the form was configured with are accepted; the rest are dropped silently.
Credits — The form owner is charged: 1 credit for a template form, or 1 per template for a collection form.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
formUuid |
path | string | Yes | UUID of the form. |
signature |
query | string | Yes | Laravel URL signature. The whole URL must be signed. |
Request body
Content-Type: multipart/form-data
| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
layers |
object | No | keys matched against the form configuration |
Layer overrides. Value keys by layer type: image -> image_url (file), text -> text, empty -> background_color, qr_code -> qr_content, barcode -> barcode_content. |
Collection forms return a bare array of snap objects, not a `data`-wrapped object.
This route has no rate limiting.
Example request
curl
curl -X POST 'https://snapsbrew.com/api/v1/embedded-forms/<formUuid>?signature=<sig>' \
-F 'layers[headline]=Summer sale' \
-F 'layers[logo][email protected]'
Example responses
{
"data": {
"id": "9f1c2d3e",
"media_url": "https://snapsbrew.com/storage/snaps/9f1c2d3e.png",
"type": "image",
"status": "completed",
"is_async": false,
"webhook_url": null,
"completed_at": "2026-05-01T12:00:03Z",
"last_error": null,
"variants": [],
"template_id": "141e8454",
"template_title": "Launch card",
"created_at": "2026-05-01T12:00:00Z"
}
}
{
"message": "Not found."
}
{
"message": "The form does not include any templates."
}