Automation API

Blobify can be managed over HTTP from scripts, CLIs, and services.

What automation can do

  • read models and blocks
  • validate schema bundles before applying them
  • import models and blocks
  • create and update drafts
  • patch drafts with JSON Patch ops (atomic, with optional optimistic concurrency)
  • bulk-import up to 100 items per request (for migrations)
  • publish and unpublish content

API keys use the same access model as members:

  • admin
  • developer
  • editor
  • viewer

Each key can be limited to specific spaces.

Workflow

  1. Create an API key in Settings → Developer → API Keys
  2. Read automation context for locales, spaces, models, and blocks
  3. List or search existing content by stable fields such as slug
  4. Generate JSON in your script or service
  5. Validate before writing
  6. Save drafts or upsert by a stable key
  7. Publish when ready

Read automation context

Use this first in import scripts. It returns the org settings that migration tools need without exposing storage credentials.

bashcode
API_URL="https://api.blobify.io"
ORG_ID="org_..."
API_KEY="bk_live_..."

curl -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/automation/context"

Example response:

jsoncode
{
  "org": {
    "id": "org_...",
    "name": "Example",
    "defaultLocale": "en",
    "locales": [{ "code": "en", "label": "English" }],
    "rootPrefix": "blobify",
    "publicBucketUrl": "https://cdn.example.com",
    "spaceSettings": {
      "main": { "contentPrefix": "content_abc123" }
    }
  },
  "spaces": ["main"],
  "models": [
    {
      "model": "article",
      "name": "Article",
      "version": 3,
      "singleton": false,
      "displayField": "title",
      "summaryFields": ["title", "slug"],
      "lookupFields": ["slug"],
      "listIndexIds": ["latest"]
    }
  ],
  "blocks": []
}

Read schema

bashcode
curl -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/schemas/models"
bashcode
curl -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/schemas/blocks"

Validate a schema bundle

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/schemas/validate-import" \
  -d '{
    "models": [
      {
        "model": "article",
        "name": "Article",
        "displayField": "title",
        "summaryFields": ["title", "slug"],
        "fields": {
          "title": { "type": "text", "required": true, "translatable": true },
          "slug": { "type": "slug", "required": true, "sourceField": "title" },
          "body": { "type": "richtext", "translatable": true }
        }
      }
    ],
    "blocks": []
  }'

Import models and blocks

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/schemas/import" \
  -d '{
    "models": [
      {
        "model": "article",
        "name": "Article",
        "displayField": "title",
        "summaryFields": ["title", "slug"],
        "fields": {
          "title": { "type": "text", "required": true, "translatable": true },
          "slug": { "type": "slug", "required": true, "sourceField": "title" },
          "body": { "type": "richtext", "translatable": true }
        }
      }
    ],
    "blocks": []
  }'

List or search content

List content from summary shards. Responses include IDs, publishing metadata, and the model's configured summary fields, so import scripts can decide whether to create, update, skip, or publish without fetching every full document.

bashcode
curl -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article?state=draft&locale=en&page=1&perPage=100"

Search by a summary or indexed field:

bashcode
curl -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article?state=draft&locale=en&field=slug&value=hello-world"

Example response:

jsoncode
{
  "items": [
    {
      "id": "cnt_...",
      "model": "article",
      "spaceId": "main",
      "state": "draft",
      "created": "2026-04-24T10:00:00.000Z",
      "updated": "2026-04-24T10:05:00.000Z",
      "updatedAt": "2026-04-24T10:05:00.000Z",
      "publishedLocales": ["en"],
      "fields": {
        "title": { "en": "Hello world" },
        "slug": "hello-world"
      }
    }
  ],
  "page": 1,
  "perPage": 100,
  "total": 1,
  "nextPage": null
}

Validate content before saving

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article/validate" \
  -d '{
    "fields": {
      "title": { "en": "Hello world" },
      "slug": "hello-world",
      "body": {
        "en": {
          "type": "root",
          "children": [
            {
              "type": "paragraph",
              "children": [{ "type": "text", "value": "Created locally" }]
            }
          ]
        }
      }
    }
  }'

Upsert by stable key

Use upsert for restartable imports. Blobify looks up an existing entry by the specified field and updates it; if none exists, it creates a new draft.

bashcode
curl -X PUT \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article/upsert" \
  -d '{
    "key": {
      "field": "slug",
      "value": "hello-world",
      "locale": "en",
      "state": "draft"
    },
    "fields": {
      "title": { "en": "Hello world, updated" },
      "slug": "hello-world"
    },
    "publish": {
      "locales": ["en"]
    }
  }'

Response:

jsoncode
{
  "action": "updated",
  "content": {
    "id": "cnt_...",
    "model": "article",
    "publishedLocales": ["en"],
    "fields": {
      "title": { "en": "Hello world, updated" },
      "slug": "hello-world"
    }
  }
}

Create a draft

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article" \
  -d '{
    "fields": {
      "title": { "en": "Hello world" },
      "slug": "hello-world",
      "body": {
        "en": {
          "type": "root",
          "children": [
            {
              "type": "paragraph",
              "children": [{ "type": "text", "value": "Created locally" }]
            }
          ]
        }
      }
    }
  }'

Bulk import

Save up to 100 items in one request. Each item is processed independently — a per-item validation failure does not abort the rest of the batch. The response carries a results array preserving input order with a status of created, updated, or failed plus the saved doc (or the error message) for each item. Designed for migrations from another CMS and large-scale agent writes; items with id upsert, items without create new.

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article/bulk" \
  -d '{
    "items": [
      { "fields": { "slug": "page-a", "title": { "en": "A" } } },
      { "id": "cnt_existing", "fields": { "slug": "page-b", "title": { "en": "B" } } },
      { "fields": { "slug": "page-c", "title": { "en": "C" } } }
    ]
  }'

Response:

jsoncode
{
  "results": [
    { "index": 0, "id": "cnt_new1", "status": "created", "doc": { ... } },
    { "index": 1, "id": "cnt_existing", "status": "updated", "doc": { ... } },
    { "index": 2, "id": "cnt_new2", "status": "failed", "error": "..." }
  ]
}

The HTTP response is always 200 when the batch as a whole is well-formed. Inspect each item's status and decide whether to retry the failures. The endpoint rejects the request with 400 only when the batch itself is malformed (empty, more than 100 items).

Patch a draft (small, atomic edits)

For surgical edits — e.g. an agent appending a single block to a long page — use JSON Patch (RFC 6902) instead of a full document update. The endpoint accepts an op array against the draft's fields object and runs the patched result through the same validation + save pipeline.

Optional If-Match: <etag> header enables optimistic concurrency: pass the ETag you read the draft at, and the API rejects with 409 if another writer has modified the draft since.

bashcode
curl -X PATCH \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article/$CONTENT_ID" \
  -d '[
    { "op": "add",     "path": "/blocks/-", "value": { "type": "callout", "id": "blk_x", "title": "New section" } },
    { "op": "replace", "path": "/title/en", "value": "New title" }
  ]'

Update a draft

bashcode
CONTENT_ID="cnt_..."

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article/$CONTENT_ID" \
  -d '{
    "fields": {
      "title": { "en": "Hello world, updated" },
      "slug": "hello-world",
      "body": {
        "en": {
          "type": "root",
          "children": [
            {
              "type": "paragraph",
              "children": [{ "type": "text", "value": "Updated locally" }]
            }
          ]
        }
      }
    }
  }'

Publish

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/main/article/$CONTENT_ID/publish" \
  -d '{
    "locales": ["en"]
  }'

Local tool pattern

  1. Fetch automation context.
  2. Fetch current schemas if you need full field definitions.
  3. List/search existing content using stable keys.
  4. Generate JSON on your machine.
  5. Validate before writing.
  6. Upsert drafts by stable key.
  7. Publish only when explicitly requested.