API Reference

Complete reference for all Blendo API v1 endpoints. For a compact table of every endpoint, see the quick-reference table.

Authentication

All API v1 requests require a Bearer token. Generate an API key in Settings → API Key or via the API Keys endpoints.

Include the key in every request as an Authorization header:

Authorization: Bearer optk_your_api_key_here

API keys are scoped to your account. All resources returned are limited to those owned by your account.

Scopes

When creating an API key, you can assign scopes to limit what the key can do:

ScopeAccess
readGET requests only
writePOST, PATCH, DELETE requests
(no scopes)Full access (read + write)

Rate Limiting

API requests are rate-limited to 60 requests per minute per IP address. If you exceed this limit, you'll receive a 429 Too Many Requests response.

Base URL

https://my.blendo.com/api/v1

All endpoint paths below are relative to this base URL.

Response Format

Single Resource

All successful responses wrap data in a data key:

{
  "data": {
    "id": "01HQ...",
    "name": "My Site",
    "slug": "my-site",
    "created_at": "2026-04-01T12:00:00Z"
  }
}

Paginated Lists

List endpoints return paginated results with a meta object:

{
  "data": [ ... ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total": 48,
    "last_page": 2
  }
}

Control pagination with query parameters:

ParameterDefaultDescription
page1Page number
per_page25Items per page (max 100)

Errors

Error responses include an error key with a human-readable message:

// 404 Not Found
{
  "error": "Not found"
}

// 422 Validation Error
{
  "message": "The name field is required.",
  "errors": {
    "name": ["The name field is required."]
  }
}

// 429 Rate Limited
{
  "error": "Too many requests."
}

Delete Responses

Successful deletes return:

{
  "data": {
    "deleted": true
  }
}

Sites

Sites are the top-level container for pages, posts, forms, and settings. Each site gets a subdomain at slug.blendo.com or a custom domain.

GET /sites

List all sites in your account.

curl https://my.blendo.com/api/v1/sites \
  -H "Authorization: Bearer optk_your_key"

Response: Paginated list with fields: id, name, slug, site_type, custom_domain, tagline, is_published, created_at, updated_at.

POST /sites

Create a new site.

curl -X POST https://my.blendo.com/api/v1/sites \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Portfolio",
    "slug": "my-portfolio",
    "site_type": "portfolio",
    "tagline": "Design work by Jane"
  }'
FieldTypeRequiredDescription
namestringYesDisplay name (max 255)
slugstringYesSubdomain slug (lowercase, alphanumeric + hyphens)
site_typestringNobrochure, portfolio, blog, or shop. Default: brochure
taglinestringNoShort description (max 500)

Response (201):

{
  "data": {
    "id": "01J5ABC...",
    "name": "My Portfolio",
    "slug": "my-portfolio",
    "site_type": "portfolio",
    "created_at": "2026-04-06T10:00:00Z"
  }
}

GET /sites/{id}

Get a single site with full details including page/post counts, branding, navigation links, and social links.

PATCH /sites/{id}

Update a site. Send only the fields you want to change.

FieldTypeDescription
namestringDisplay name
slugstringSubdomain slug
taglinestringShort description
site_typestringbrochure, portfolio, blog, shop
favicon_urlstringURL to favicon image
og_image_urlstringDefault Open Graph image
logo_urlstringLogo image URL
logo_textstringText-based logo
meta_title_suffixstringAppended to all page titles (e.g. | My Brand)
google_analytics_idstringGA measurement ID (G-XXXXXX)
nav_linksarrayArray of {label, url, external}
social_linksarrayArray of {platform, url}
timezonestringIANA timezone (e.g. America/Chicago)
languagestringLanguage code (e.g. en)
is_publishedbooleanToggle site visibility
brand_idstringAssociate a brand guideline set

DELETE /sites/{id}

Delete a site and all its pages. This action is irreversible.

curl -X DELETE https://my.blendo.com/api/v1/sites/01J5ABC... \
  -H "Authorization: Bearer optk_your_key"

Pages

Pages contain HTML content, SEO metadata, and version history. Pages belong to a site.

GET /pages

List all pages across your account. Returns: id, title, slug, pathway, status, published_at, created_at, updated_at.

curl https://my.blendo.com/api/v1/pages \
  -H "Authorization: Bearer optk_your_key"

GET /pages/{id}

Get a single page with full content, SEO data, and status.

curl https://my.blendo.com/api/v1/pages/01J5ABC... \
  -H "Authorization: Bearer optk_your_key"

Response:

{
  "page": {
    "id": "01J5ABC...",
    "title": "Launch Event",
    "slug": "launch-event",
    "pathway": "event",
    "status": "published",
    "content": {
      "html": "<style>...</style><section>...</section>",
      "google_fonts": "Inter:400,700",
      "title": "Launch Event",
      "meta_description": "Join us for..."
    },
    "seo": {
      "meta_title": "Launch Event",
      "meta_description": "Join us for..."
    },
    "published_at": "2026-04-01T12:00:00Z",
    "created_at": "2026-04-01T11:00:00Z",
    "updated_at": "2026-04-01T12:00:00Z"
  }
}

PATCH /pages/{id}

Update a page. Creates a version snapshot before saving content changes.

FieldTypeDescription
titlestringPage title
contentobjectPage content (html, google_fonts, meta_description)
block_treearrayBlock-format content (alternative to html content)
seoobjectSEO metadata (meta_title, meta_description)

POST /pages/{id}/publish

Publish a page. Renders the HTML and deploys it to the edge CDN.

curl -X POST https://my.blendo.com/api/v1/pages/01J5ABC.../publish \
  -H "Authorization: Bearer optk_your_key"

POST /pages/{id}/unpublish

Unpublish a page. Removes it from the edge CDN and sets status to draft.

GET /pages/{id}/versions

List all saved versions of a page, ordered by version number descending.

{
  "versions": [
    {
      "id": "01J5XYZ...",
      "version": 3,
      "change_summary": "API update",
      "created_at": "2026-04-05T14:30:00Z"
    }
  ]
}

Posts

Blog posts with markdown content, tags, and author information. Posts belong to a site.

GET /sites/{site_id}/posts

List posts for a site. Filter by ?status=published or ?status=draft.

curl https://my.blendo.com/api/v1/sites/01J5ABC.../posts \
  -H "Authorization: Bearer optk_your_key"

POST /sites/{site_id}/posts

Create a new post on a site.

curl -X POST https://my.blendo.com/api/v1/sites/01J5ABC.../posts \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Getting Started with Blendo",
    "body": "# Welcome\n\nThis is your first post...",
    "tags": ["tutorial", "getting-started"],
    "status": "draft"
  }'
FieldTypeRequiredDescription
titlestringYesPost title (max 255)
bodystringYesMarkdown content
slugstringNoURL slug (auto-generated from title if omitted)
excerptstringNoShort summary (max 1000)
tagsarrayNoArray of tag strings
authorstringNoAuthor name (defaults to your name)
featured_imagestringNoImage URL
statusstringNodraft or published. Default: draft

GET /posts/{id}

Get a single post with full body content.

PATCH /posts/{id}

Update a post. Send only the fields you want to change. Same fields as create.

DELETE /posts/{id}

Permanently delete a post.

POST /posts/{id}/publish

Set the post status to published and set published_at if not already set.

Media

Upload and manage images via a presigned URL flow. Images are stored on Cloudflare R2 and served from the CDN at cdn.blendo.site.

GET /media

List media items in your account. Returns: id, filename, cdn_url, mime_type, file_size, width, height, alt_text, tags, created_at.

GET /media/{id}

Get a single media item with full metadata including license and attribution info.

POST /media/presign

Request a presigned upload URL. Upload your file directly to R2 using the returned URL, then confirm the upload.

curl -X POST https://my.blendo.com/api/v1/media/presign \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "hero-banner.jpg",
    "mime_type": "image/jpeg",
    "file_size": 245000
  }'
FieldTypeRequiredDescription
filenamestringYesOriginal filename
mime_typestringYesimage/jpeg, image/png, image/gif, image/webp, image/svg+xml
file_sizeintegerYesFile size in bytes (max 10 MB)

Response: Returns upload_url (PUT to this URL with the raw file) and r2_key.

POST /media/confirm

Confirm an upload after putting the file to the presigned URL. This creates the media item record.

FieldTypeRequiredDescription
r2_keystringYesThe key from the presign response
filenamestringYesOriginal filename
mime_typestringYesMIME type
file_sizeintegerYesFile size in bytes
widthintegerNoImage width in pixels
heightintegerNoImage height in pixels
alt_textstringNoAlt text for accessibility

PATCH /media/{id}

Update media metadata (alt text and tags only).

DELETE /media/{id}

Delete a media item and remove it from storage.

Forms

Create and manage forms with custom fields. Forms collect submissions and can trigger email notifications.

GET /forms

List all forms with submission counts.

GET /forms/{id}

Get a form with its field definitions, settings, and unread submission count.

POST /forms

Create a new form.

curl -X POST https://my.blendo.com/api/v1/forms \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "01J5ABC...",
    "name": "Contact Form",
    "type": "contact",
    "fields": [
      {"key": "name", "label": "Name", "type": "text", "required": true},
      {"key": "email", "label": "Email", "type": "email", "required": true},
      {"key": "message", "label": "Message", "type": "textarea", "required": true}
    ]
  }'
FieldTypeRequiredDescription
site_idstringYesThe site this form belongs to
namestringYesForm name (max 255)
typestringYescontact, subscribe, register, or custom
fieldsarrayYesArray of field definitions (key, label, type, required)
settingsobjectNoForm settings (notification email, redirect URL, etc.)

Supported field types: text, email, textarea, select, radio, checkbox, phone, url, number, date, time, hidden, html_block, section_break, page_break, multi_select, name, consent.

PATCH /forms/{id}

Update a form's name, fields, settings, or active status.

DELETE /forms/{id}

Delete a form and all its submissions.

GET /forms/{id}/submissions

List submissions for a specific form (excluding spam). Paginated.

curl https://my.blendo.com/api/v1/forms/01J5ABC.../submissions \
  -H "Authorization: Bearer optk_your_key"

Submissions & Subscribers (Legacy)

These endpoints return submissions and subscribers across all your pages. For form-specific submissions, use the Forms endpoints instead.

GET /submissions

List form submissions across all pages. Filter with ?page_id=... and ?action=....

GET /subscribers

List email subscribers across all sites.

Brands

Brand guidelines define your visual identity: colors, typography, buttons, inputs, and layout rules. Brands are account-level and can be attached to one or more sites.

GET /brands

List all brands in your account.

GET /brands/{id}

Get a brand with full guidelines and connected site count.

POST /brands

Create a brand.

curl -X POST https://my.blendo.com/api/v1/brands \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp",
    "guidelines": {
      "colors": {"primary": "#EF233C", "secondary": "#2563EB", "background": "#FFFFFF"},
      "typography": {"heading": "Space Grotesk", "body": "Inter"},
      "buttons": {"radius": "8px", "style": "filled"},
      "inputs": {"radius": "6px", "border": "1px solid #E2E8F0"},
      "layout": {"max_width": "1200px", "spacing": "relaxed"}
    }
  }'
FieldTypeRequiredDescription
namestringYesBrand name (max 100)
logo_urlstringNoLogo image URL
guidelinesobjectYesContains: colors, typography, buttons, inputs, layout

PATCH /brands/{id}

Update a brand's name, logo, or guidelines.

DELETE /brands/{id}

Delete a brand. Sites using this brand will be unlinked. If the deleted brand was the default, the next brand becomes default.

Templates

Browse and use pre-built page templates. Templates are read-only; you can use them to create new pages.

GET /templates

List available templates. Returns: id, title, slug, pathway, purpose, description, style_family, features, is_featured, use_count, thumbnail_path.

POST /templates/{slug}/use

Create a new draft page from a template. If you don't have a site yet, one is created automatically.

curl -X POST https://my.blendo.com/api/v1/templates/community-garden/use \
  -H "Authorization: Bearer optk_your_key"

Response (201):

{
  "data": {
    "page_id": "01J5XYZ...",
    "site_id": "01J5ABC...",
    "title": "Community Garden",
    "slug": "community-garden",
    "status": "draft"
  }
}

Navigation menus for site headers, footers, and mobile navigation.

GET /sites/{site_id}/menus

List all menus for a site.

POST /sites/{site_id}/menus

Create a menu. Only one menu per location is allowed.

curl -X POST https://my.blendo.com/api/v1/sites/01J5ABC.../menus \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Main Navigation",
    "location": "header",
    "items": [
      {"label": "Home", "url": "/"},
      {"label": "About", "url": "/about/"},
      {"label": "Blog", "url": "/blog/"}
    ]
  }'
FieldTypeRequiredDescription
namestringYesMenu name (max 100)
locationstringYesheader, footer, mobile, or sidebar
itemsarrayNoArray of menu items with label and url

PATCH /menus/{id}

Update a menu's name or items.

DELETE /menus/{id}

Delete a menu.

Redirects

Manage URL redirects for your site. Useful for preserving SEO when restructuring pages.

GET /sites/{site_id}/redirects

List redirects for a site. Filter by ?search= to match source or target URLs.

POST /sites/{site_id}/redirects

Create a redirect rule.

curl -X POST https://my.blendo.com/api/v1/sites/01J5ABC.../redirects \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source_path": "/old-page/",
    "target_url": "/new-page/",
    "status_code": 301
  }'
FieldTypeRequiredDescription
source_pathstringYesPath to redirect from (must start with /)
target_urlstringYesDestination URL or path
status_codeintegerNo301 (permanent) or 302 (temporary). Default: 301

PATCH /redirects/{id}

Update a redirect's source, target, status code, or active state.

DELETE /redirects/{id}

Delete a redirect.

Webhooks

Receive real-time notifications when events happen on your site. Webhooks are signed with a secret for verification.

GET /webhooks

List all webhooks. Filter by ?site_id=....

POST /webhooks

Create a webhook endpoint.

curl -X POST https://my.blendo.com/api/v1/webhooks \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "01J5ABC...",
    "url": "https://example.com/webhooks/blendo",
    "events": ["site.updated", "post.created", "form.submitted"],
    "description": "Sync to CRM"
  }'

Response (201): Includes a secret field (starts with whsec_) that is only shown at creation time. Use it to verify webhook signatures.

Save your webhook secret immediately. It is only returned once at creation time and cannot be retrieved later. If you lose it, delete and recreate the webhook.

PATCH /webhooks/{id}

Update a webhook's URL, events, headers, description, or active state.

DELETE /webhooks/{id}

Delete a webhook.

Analytics

Query aggregated analytics data: pageviews, CTA clicks, unique visitors, referrers, and per-page breakdown.

GET /analytics

Get account-wide analytics.

curl "https://my.blendo.com/api/v1/analytics?date_from=2026-03-01&date_to=2026-03-31&granularity=day" \
  -H "Authorization: Bearer optk_your_key"
ParameterDefaultDescription
date_from7 days agoStart date (YYYY-MM-DD)
date_toTodayEnd date (YYYY-MM-DD)
granularitydayday or hour

Response:

{
  "data": {
    "totals": {
      "pageviews": 12540,
      "cta_clicks": 834,
      "unique_visitors": 8320
    },
    "time_series": [
      {"period": "2026-03-01", "pageviews": 420, "cta_clicks": 28, "unique_visitors": 280}
    ],
    "top_referrers": {
      "google.com": 3200,
      "twitter.com": 890,
      "direct": 4230
    },
    "top_pages": [
      {"page_id": "01J5...", "title": "Home", "slug": "home", "pageviews": 5400, "cta_clicks": 312}
    ]
  }
}

GET /analytics/pages/{page_id}

Get analytics for a specific page. Same parameters and response structure as above, plus top_ctas (most-clicked buttons).

Experiments (A/B Testing)

Create and manage A/B test experiments on your pages.

GET /experiments

List all experiments with their page info and variants.

GET /experiments/{id}

Get a single experiment with variants and winner info.

POST /experiments

Create a new experiment. A "Control" variant is automatically created from the page's current content.

curl -X POST https://my.blendo.com/api/v1/experiments \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "page_id": "01J5ABC...",
    "name": "Hero CTA Test",
    "goal": "cta_click",
    "traffic_pct": 50
  }'
FieldTypeRequiredDescription
page_idstringYesPage to run the experiment on
namestringYesExperiment name (max 255)
goalstringYescta_click or form_submit
traffic_pctintegerYesPercentage of traffic included (10-100)

POST /experiments/{id}/start

Start running the experiment. Requires at least 2 variants.

POST /experiments/{id}/stop

Pause the experiment.

POST /experiments/{id}/complete

Complete the experiment and apply the winning variant's content to the page.

curl -X POST https://my.blendo.com/api/v1/experiments/01J5.../complete \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"winner_variant_id": "01J5XYZ..."}'

Newsletters

Create, schedule, and send newsletters to your subscribers.

GET /newsletters

List newsletters. Filter by ?status=draft, ?status=sent, or ?status=scheduled.

GET /newsletters/{id}

Get a newsletter with segment information.

POST /newsletters

Create a newsletter.

curl -X POST https://my.blendo.com/api/v1/newsletters \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "March Update",
    "body": "# March Update\n\nHere is what happened this month...",
    "excerpt": "A quick recap of March.",
    "tags": ["monthly", "update"]
  }'
FieldTypeRequiredDescription
titlestringYesNewsletter title
bodystringYesMarkdown content
excerptstringNoShort summary (max 500)
featured_imagestringNoHeader image URL
authorstringNoAuthor name
tagsarrayNoTag array
segment_idstringNoSend only to this subscriber segment
is_premiumbooleanNoMark as premium/paid content

PATCH /newsletters/{id}

Update a draft newsletter. Cannot edit sent newsletters.

POST /newsletters/{id}/send

Send the newsletter immediately to all matching subscribers.

POST /newsletters/{id}/schedule

Schedule the newsletter for later delivery.

curl -X POST https://my.blendo.com/api/v1/newsletters/01J5.../schedule \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"scheduled_at": "2026-04-10T09:00:00Z"}'

Invoices

Create and manage invoices for customers.

GET /invoices

List invoices. Filter by ?status=unpaid, ?status=overdue, ?status=paid, ?status=draft, ?status=void.

GET /invoices/{id}

Get a single invoice with customer, line items, booking, and receipt details.

POST /invoices

Create an invoice with line items.

curl -X POST https://my.blendo.com/api/v1/invoices \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "01J5ABC...",
    "customer_id": "01J5CUS...",
    "due_date": "2026-04-20",
    "items": [
      {"description": "Website design", "quantity": 1, "unit_price": 250000},
      {"description": "Hosting (annual)", "quantity": 1, "unit_price": 34800}
    ]
  }'
Prices are in cents. unit_price: 250000 = $2,500.00. All monetary values in the API use the smallest currency unit.

POST /invoices/{id}/send

Send the invoice to the customer via email. Requires at least one line item.

POST /invoices/{id}/mark-paid

Mark an invoice as paid. Optionally specify payment method: ?payment_method=cash (default), stripe, or comp.

POST /invoices/{id}/void

Void an invoice (irreversible).

Membership Plans

Define membership tiers for your site (subscription, punch-card packs, or time-limited passes).

GET /membership-plans

List all plans with active/total member counts.

POST /membership-plans

Create a membership plan.

FieldTypeRequiredDescription
site_idstringYesSite ID
namestringYesPlan name
typestringYessubscription, pack, or pass
priceintegerYesPrice in cents
billing_intervalstringNomonth or year (for subscriptions)
trial_daysintegerNoFree trial duration
credit_amountintegerNoNumber of credits (for packs)
valid_daysintegerNoExpiration in days (for passes)
access_levelstringNobasic, premium, or vip
featuresarrayNoArray of feature description strings

PATCH /membership-plans/{id}

Update a plan's name, price, features, or active status.

DELETE /membership-plans/{id}

Delete a plan. Fails if there are active members on it.

Memberships

Enroll customers in membership plans and manage their lifecycle.

GET /memberships

List all memberships with plan and customer details. Filter by ?status=active, ?status=paused, ?status=cancelled.

POST /memberships

Enroll a customer in a plan.

curl -X POST https://my.blendo.com/api/v1/memberships \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "plan_id": "01J5PLN...",
    "customer_id": "01J5CUS...",
    "payment_method": "stripe"
  }'

POST /memberships/{id}/pause

Pause a membership (preserves remaining credits/time).

POST /memberships/{id}/resume

Resume a paused membership.

POST /memberships/{id}/cancel

Cancel a membership.

Events

Create and manage events with ticket tiers, venues, and check-in.

GET /events

List events. Filter by ?status=draft, ?status=published, ?status=cancelled, ?status=completed.

GET /events/{id}

Get an event with venue, ticket tiers, sessions, and ticket statistics (total, valid, used, cancelled, revenue, check-in rate).

POST /events

Create an event.

curl -X POST https://my.blendo.com/api/v1/events \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "01J5ABC...",
    "name": "Summer Gala",
    "event_type": "single",
    "start_at": "2026-07-15T18:00:00Z",
    "end_at": "2026-07-15T23:00:00Z",
    "capacity": 200,
    "tiers": [
      {"name": "General Admission", "price": 5000, "quantity_available": 150},
      {"name": "VIP", "price": 15000, "quantity_available": 50}
    ]
  }'
FieldTypeRequiredDescription
site_idstringYesSite ID
namestringYesEvent name
event_typestringYessingle, multi_day, or series
start_atdatetimeYesEvent start time (ISO 8601)
end_atdatetimeNoEvent end time
capacityintegerYesMax attendees
tiersarrayNoTicket tiers (name, price in cents, quantity_available)
refund_policystringNoflexible, moderate, strict, or none

PATCH /events/{id}

Update an event's details, status, or settings.

DELETE /events/{id}

Delete an event. Fails if there are active tickets.

POST /events/{id}/publish

Publish an event. Requires at least one ticket tier.

Courses

Create and manage online courses with modules and lessons.

GET /courses

List courses. Filter by ?status=draft, ?status=published, ?status=archived.

GET /courses/{id}

Get a course with modules, lessons, and linked product info.

POST /courses

Create a course.

FieldTypeRequiredDescription
titlestringYesCourse title
descriptionstringNoFull description
enrollment_typestringYesopen, paid, or invite_only
instructor_namestringNoInstructor name
difficulty_levelstringNobeginner, intermediate, or advanced
product_idstringNoLinked product for paid enrollment

PATCH /courses/{id}

Update a course. Supports all create fields plus status, certificate_enabled, drip_enabled, discussion_enabled.

DELETE /courses/{id}

Archive a course (soft delete).

Products

Manage your product catalog for e-commerce.

GET /products

List products. Filter by ?status=active, ?collection=ID, ?search=term.

GET /products/{id}

Get a product with variants and collection associations.

POST /products

Create a product.

curl -X POST https://my.blendo.com/api/v1/products \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Classic T-Shirt",
    "product_type": "physical",
    "price": 2999,
    "description": "100% organic cotton.",
    "track_inventory": true,
    "inventory_quantity": 50,
    "tags": ["clothing", "summer"]
  }'
FieldTypeRequiredDescription
titlestringYesProduct title
product_typestringYesphysical, digital, or service
priceintegerYesPrice in cents
descriptionstringNoProduct description
compare_at_priceintegerNoOriginal price (for showing savings)
imagesarrayNoArray of image URLs
tagsarrayNoTag array
track_inventorybooleanNoEnable inventory tracking
inventory_quantityintegerNoStock count
billing_typestringNoone_time or recurring
billing_intervalstringNoweekly, monthly, or yearly (for recurring)
access_typestringNonone, lifetime, or subscription

PATCH /products/{id}

Update a product. Supports all create fields plus status (draft, active, archived) and a variants array for managing product variants inline.

DELETE /products/{id}

Archive a product (soft delete).

Orders

View and manage customer orders.

GET /orders

List orders. Filter by ?status=, ?date_from=, ?date_to=, ?search= (email or order number).

GET /orders/{id}

Get a single order with line items, customer, addresses, and fulfillment details.

PATCH /orders/{id}/fulfill

Mark an order (or specific items) as fulfilled.

curl -X PATCH https://my.blendo.com/api/v1/orders/01J5ORD.../fulfill \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "tracking_company": "USPS",
    "tracking_number": "9400111899223...",
    "tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=9400111899223..."
  }'

PATCH /orders/{id}/refund

Refund an order (full or partial).

curl -X PATCH https://my.blendo.com/api/v1/orders/01J5ORD.../refund \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"amount": 2999, "reason": "Customer requested"}'

PATCH /orders/{id}/cancel

Cancel an order. Restores inventory for tracked products.

Discounts

Create discount codes for your store.

GET /discounts

List discounts. Filter by ?active=true or ?active=false.

POST /discounts

Create a discount code.

FieldTypeRequiredDescription
titlestringYesInternal name
codestringNoDiscount code (auto-generated if omitted)
typestringYespercentage, fixed_amount, or free_shipping
valueintegerYesPercentage (e.g. 20 = 20%) or fixed amount in cents
minimum_amountintegerNoMinimum cart total in cents
usage_limitintegerNoMax total uses
once_per_customerbooleanNoLimit to one use per customer
starts_atdatetimeNoActivation date
ends_atdatetimeNoExpiration date

PATCH /discounts/{id}

Update a discount.

DELETE /discounts/{id}

Delete a discount.

Collections

Organize content and products into collections with custom schemas.

GET /collections

List all collections with entry counts.

GET /collections/{id}

Get a collection with schema, settings, and entry count.

POST /collections

Create a collection.

FieldTypeRequiredDescription
site_idstringYesSite ID
namestringYes*Collection name (*required unless using a preset)
presetstringNoCreate from a preset template
schemaarrayNoCustom field schema

PATCH /collections/{id}

Update a collection's name, description, schema, or settings.

DELETE /collections/{id}

Delete a collection and all its entries.

GET /collections/{id}/entries

List entries in a collection (paginated).

POST /collections/{id}/entries

Create an entry in a collection.

curl -X POST https://my.blendo.com/api/v1/collections/01J5COL.../entries \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "name": "Featured Article",
      "url": "https://example.com/article",
      "description": "A great read about..."
    },
    "status": "published"
  }'

PATCH /entries/{id}

Update an entry's data, status, or sort order.

DELETE /entries/{id}

Delete an entry.

Booking

Manage services, staff, locations, customers, and appointments for scheduling-based businesses.

Booking Services

MethodEndpointDescription
GET/booking/servicesList booking services
POST/booking/servicesCreate a service
GET/booking/services/{id}Get a service
PATCH/booking/services/{id}Update a service
DELETE/booking/services/{id}Delete a service

Booking Staff

MethodEndpointDescription
GET/booking/staffList staff members
POST/booking/staffAdd a staff member
GET/booking/staff/{id}Get staff details
PATCH/booking/staff/{id}Update staff
DELETE/booking/staff/{id}Remove staff

Booking Locations

MethodEndpointDescription
GET/booking/locationsList locations
POST/booking/locationsCreate a location
GET/booking/locations/{id}Get a location
PATCH/booking/locations/{id}Update a location
DELETE/booking/locations/{id}Delete a location

Customers

Unified customer records across bookings, forms, and commerce.

GET /customers

List customers. Search by ?search= (name, email, or phone).

GET /customers/{id}

Get a customer with full details including lifetime spend, visit count, and no-show count.

POST /customers

Create a customer.

PATCH /customers/{id}

Update a customer.

DELETE /customers/{id}

Delete a customer.

Appointments

Schedule and manage appointments.

GET /appointments

List appointments. Filter by ?status=, ?date=, ?date_from=, ?date_to=, ?staff_id=.

GET /appointments/{id}

Get a single appointment with service, staff, customer, and location details.

POST /appointments

Create an appointment.

PATCH /appointments/{id}

Update an appointment.

POST /appointments/{id}/cancel

Cancel an appointment.

POST /appointments/{id}/complete

Mark an appointment as completed.

POST /appointments/{id}/assign

Assign a staff member to an appointment.

Bookings & Insights (AI-Queryable)

These endpoints are designed for AI agent integration, returning pre-formatted data for conversational interfaces.

GET /bookings

List bookings with related service, staff, customer, and location data. Filter by ?status=, ?date=, ?staff_id=, ?since=.

GET /bookings/dispatch

Get current dispatch state for home service businesses — pending assignments, in-progress jobs, staff availability.

GET /bookings/stats

Booking statistics: today's bookings, revenue, utilization, no-show rate.

POST /bookings/{id}/assign

Assign a technician/staff member to a dispatch booking.

GET /customers

List customers (same as Customers section).

GET /form-submissions

List form submissions (for AI context).

GET /links

List short links created by the account.

POST /links

Create a short link.

GET /insights

Get AI-generated business insights.

GET /score

Get the business health score.

GET /revenue-feed

Revenue event feed (payments, refunds, tips).

GET /brief

Get the daily business brief (summary of today's activity, upcoming appointments, pending tasks).

API Keys

Manage API keys programmatically.

GET /api-keys

List all API keys for your account. Returns: id, name, key_prefix, scopes, is_system, last_used_at, last_used_ip, request_count, expires_at, created_at.

curl https://my.blendo.com/api/v1/api-keys \
  -H "Authorization: Bearer optk_your_key"

POST /api-keys

Create a new API key.

curl -X POST https://my.blendo.com/api/v1/api-keys \
  -H "Authorization: Bearer optk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI/CD Deploy Key",
    "scopes": ["write"],
    "expires_in_days": 90
  }'
FieldTypeRequiredDescription
namestringYesKey name (max 255)
scopesarrayNoPermission scopes (e.g. ["read"])
expires_in_daysintegerNoAuto-expire after N days (1-365)

Response (201):

{
  "data": {
    "id": 42,
    "name": "CI/CD Deploy Key",
    "key": "optk_abc123def456...",
    "key_prefix": "optk_abc1",
    "scopes": ["write"],
    "expires_at": "2026-07-05T10:00:00Z"
  }
}
The full key is only shown once. Copy it immediately after creation. You will not be able to retrieve it later — only the prefix is stored.

DELETE /api-keys/{id}

Revoke an API key. Takes effect immediately — all requests using this key will fail.

GET /api-keys/{id}/usage

Get usage statistics for a key: total request count, last used timestamp, and last used IP.

{
  "data": {
    "id": 42,
    "name": "CI/CD Deploy Key",
    "request_count": 1847,
    "last_used_at": "2026-04-06T08:32:00Z",
    "last_used_ip": "203.0.113.42",
    "created_at": "2026-04-01T10:00:00Z"
  }
}