Automation API

Use Blobify's HTTP API from migration scripts, CLIs, CI jobs, and services. Create a bearer API key in Settings > Developer > API Keys and limit it to the smallest role and set of spaces the job needs.

The role hierarchy is viewer, editor, developer, and admin. Content writes require editor or higher. Schema writes and rebuilds require developer or admin.

bashcode
API_URL="https://api.blobify.io"
ORG_ID="org_abc"
SPACE_ID="main"
API_KEY="YOUR_API_KEY"

Every example below uses this header:

httpcode
Authorization: Bearer YOUR_API_KEY

Read automation context

Start an import by reading the organization context. It returns the visible spaces, locales, model summaries, block summaries, public bucket URL, and per-space contentPrefix settings without exposing bucket credentials.

bashcode
curl -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/automation/context"
jsoncode
{
  "org": {
    "id": "org_abc",
    "name": "Example",
    "defaultLocale": "en",
    "locales": [{ "code": "en", "label": "English" }],
    "rootPrefix": "blobify",
    "publicBucketUrl": "https://cdn.example.com",
    "spaceSettings": {
      "main": { "contentPrefix": "site_a1" }
    }
  },
  "spaces": ["main"],
  "models": [
    {
      "model": "article",
      "name": "Article",
      "version": 3,
      "singleton": false,
      "displayField": "title",
      "summaryFields": ["title", "slug"],
      "lookupFields": ["slug"],
      "listIndexIds": ["latest"]
    }
  ],
  "blocks": []
}

lookupFields in this response is a compact list derived from the model's current fieldIndexes configuration.

Read and validate schemas

Read full schema definitions when your script needs field shapes:

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

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

Validate a model and block bundle without writing it:

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", "unique": true },
          "body": { "type": "richtext", "translatable": true }
        }
      }
    ],
    "blocks": []
  }'

The response includes conflicts, normalized schemas, rebuild implications, and validation errors. Apply the same bundle with POST /v1/orgs/{orgId}/schemas/import. Add "override": true only when replacing conflicting schemas is intentional.

List or find content

The authenticated list endpoint reads summary shards. It is intended for the dashboard, migration tools, and automation, not as a website runtime API.

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

Filter by an exact field and value by providing both query parameters:

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

The default state is draft, the default page is 1, and the default perPage is 100. perPage is capped at 500. The response includes items, page, perPage, total, and a numeric nextPage or 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/$SPACE_ID/article/validate" \
  -d '{
    "fields": {
      "title": { "en": "Hello world" },
      "slug": "hello-world",
      "body": {
        "en": {
          "type": "root",
          "children": [
            {
              "type": "paragraph",
              "children": [{ "type": "text", "value": "Created locally" }]
            }
          ]
        }
      }
    }
  }'

The response is { "valid": true, "errors": [] } when validation succeeds.

Create and update drafts

Create a draft and let Blobify generate its content ID:

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/$SPACE_ID/article" \
  -d '{
    "fields": {
      "title": { "en": "Hello world" },
      "slug": "hello-world"
    }
  }'

Update a draft by posting fields to its ID. Fields omitted from the request remain unchanged. Each field you do send replaces that field's stored value, so send the complete locale map for a translatable field:

bashcode
CONTENT_ID="cnt_123"

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/$SPACE_ID/article/$CONTENT_ID" \
  -d '{
    "fields": {
      "title": { "en": "Hello world, updated" },
      "slug": "hello-world"
    }
  }'

Upsert by a stable field

Use upsert for restartable imports. Blobify resolves the key through a field index or summaries, then falls back to scanning source documents if derived output is still catching up.

bashcode
curl -X PUT \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/$SPACE_ID/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"] }
  }'

The response has action: "created" or action: "updated" plus the saved content document. If locale is omitted, the organization default locale is used.

Patch draft fields

PATCH accepts an RFC 6902 operation array against the draft's fields object. Paths therefore start at a field name, not at /fields.

bashcode
curl -X PATCH \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  "$API_URL/v1/orgs/$ORG_ID/content/$SPACE_ID/article/$CONTENT_ID" \
  -d '[
    { "op": "replace", "path": "/title/en", "value": "A corrected title" },
    { "op": "add", "path": "/excerpt", "value": { "en": "Short summary" } }
  ]'

The patched fields pass through the normal validation and save pipeline. An optional If-Match: <etag> header enables optimistic concurrency. A stale ETag returns 409 Conflict instead of overwriting another writer.

Bulk-save drafts

The bulk endpoint accepts 1 to 100 items. Items with an id update that draft. Items without an id create a draft. Each item is processed independently.

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

A well-formed batch returns HTTP 200 even when an individual item fails. Inspect every result's status, which is created, updated, or failed. A malformed or empty batch, or a batch above 100 items, returns 400.

Publish and unpublish

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

Unpublish selected locales with the matching /unpublish endpoint. Omitting locales unpublishes all currently published locales.

Archive, restore, and permanently delete

The first delete of a live entry archives it:

bashcode
curl -X DELETE \
  -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/content/$SPACE_ID/article/$CONTENT_ID"

Restore an archived entry with POST .../$CONTENT_ID/restore. Permanently delete by issuing DELETE again while the entry is already in the archive. Keep this archive-first sequence in automation so accidental deletion remains recoverable.

Rebuild summaries and indexes

Rebuild routes are asynchronous. They return 202 Accepted after placing a model-level job on the queue worker.

bashcode
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  "$API_URL/v1/orgs/$ORG_ID/content/$SPACE_ID/article/rebuild-all?state=published"

Replace rebuild-all with rebuild-summaries, rebuild-field-indexes, or rebuild-list-indexes for a narrower job. Omit state to rebuild both draft and published output. The equivalent routes under /schemas/models/{model}/ enqueue work for every space.

Recommended migration loop

  1. Read automation context and full schemas.
  2. List or filter existing content by a stable key.
  3. Validate schema and content payloads locally before writing.
  4. Upsert restartable items by a stable indexed field.
  5. Inspect every bulk result.
  6. Publish only the locales that should go live.
  7. Rebuild derived output after schema or index configuration changes, then wait for the asynchronous job to converge.