Production synchronization

Use this protected machine-client route to validate or synchronize a complete publication bundle. It is intended for an authorized source machine and a target publication owner; it is not a replacement for the single-document import workflow.

Authentication

POST /import/production requires a bearer token with all of these properties:

  • Its client ID matches the source machine ID in the bundle and the source machine configured for the service.
  • Its principal_type is oauth_client and its machine_owner_proxy claim is true.
  • Its aud claim contains the configured production-import audience. The default is https://api.blog.service.m7.org/v1/; an installation can configure a different audience, which must match exactly.

For an existing target publication, the authenticated proxy principal also needs import.production. When the target publication does not exist, the principal needs publication.create.

Route

Route Required fields Optional fields Success result
POST /import/production bundle apply, sync_mode Dry-run plan or applied synchronization summary.

Request fields

Field Required Type Description
bundle Yes Object m7_blog_production_bundle version 1, at most 100 MiB when JSON encoded.
apply No Boolean Defaults to false. Set true only after reviewing a dry run.
sync_mode No merge or overwrite Defaults to merge.

The bundle must contain:

Bundle field Required Description
format Yes Exactly m7_blog_production_bundle.
version Yes Exactly 1.
source.machine_id Yes UUID that matches the authenticated client ID.
publication.source_id Yes Source publication UUID.
publication.slug Yes Target publication slug.
publication.name Yes Target publication name.
data Yes Publication data object.
integrity.data_sha256 Yes Lowercase SHA-256 of the final compact JSON encoding of data.

data is required, but its sections are optional. Omitted record sections behave as empty arrays, and omitted settings behaves as an empty settings value. With merge, omitted sections leave target records unchanged; with overwrite, the bundle is authoritative and empty sections remove the corresponding target records.

Every record section is an array. Rows in every section except post_authors and post_tags require a unique source_id UUID. References use these source IDs and must point to a record included in the same bundle.

Data records and references

Use the following fields for a complete bundle. Fields described as optional may be omitted from a row; all other fields in the table are required when that row type is present.

Section Required fields Optional fields
authors source_id, slug, name bio, avatar_media_source_id, external_url, socials, metadata
categories source_id, slug, name description, summary, active, featured_media_source_id, seo, metadata
tags source_id, slug, name description, seo, metadata
posts source_id, slug, title excerpt, featured_media_source_id, category_source_id, body, body_format, status, visibility, published_at, scheduled_at, archived, archive_note
post_authors post_source_id, author_source_id position
post_tags post_source_id, tag_source_id
pages source_id, slug, title body, body_format, status, visibility, published_at, archived, archive_note
redirects source_id, source_path, destination_path status_code
media_items source_id, kind, state title, alt_text, caption, metadata, deleted_at
media_renditions source_id, media_source_id, source, state, role asset_id, external_url, original_filename, mime_type, byte_size, width, height, duration_ms, metadata, deleted_at
content_media source_id, media_source_id, content_type, content_source_id usage, sort_order, alt_text, caption, metadata
domain_verifications source_id, host, verification_method, token, status, expires_at last_checked_at, verified_at
domains source_id, host, verification_source_id, verification_method, verified_at
revisions source_id, content_type, content_source_id, kind, snapshot
imports source_id, source_name, detected_format, state source_content, parsed_payload, parse_errors, applied_post_source_id
exports source_id, format, state selection, archive_filename

settings is optional. Send an object to update settings; omit it or use an empty array when it has no settings to apply. publication.featured_media_source_id, avatar_media_source_id, featured_media_source_id, category_source_id, media_source_id, content_source_id, verification_source_id, and applied_post_source_id are source-ID references; omit or set an optional reference to null when it has no target.

Posts use draft, review, scheduled, or published status; pages use draft or published; and their visibility is public, unlisted, or private. Media items use draft, ready, failed, or deleted; renditions use bigfs or external as source and reserved, ready, failed, or deleted as state. Domain verification methods are http or dns, and verification status is pending, verified, or expired. Revisions use saved or autosave; imports use markdown_front_matter or unknown and ready, invalid, applied, or cancelled; exports use markdown_zip and ready or cancelled.

Date-time fields use YYYY-MM-DD HH:MM:SS with optional microseconds, or null where allowed. The import does not preserve an exported archived_at; it applies the requested archived state and records the target-side archival time.

Dry run and apply

Start with a dry run. This is a structurally valid empty bundle; the checksum is the SHA-256 of its compact data value, {"settings":[]}. Replace the fake UUIDs with the configured machine ID and your publication source ID before sending it.

{
  "bundle": {
    "format": "m7_blog_production_bundle",
    "version": 1,
    "source": { "machine_id": "11111111-1111-1111-1111-111111111111" },
    "publication": {
      "source_id": "22222222-2222-2222-2222-222222222222",
      "slug": "engineering-notes",
      "name": "Engineering Notes"
    },
    "integrity": { "data_sha256": "845d7dd3ba293b9cb483a452d4f0c3af713d47eb9bf5e08176d6a68322d8126d" },
    "data": { "settings": [] }
  },
  "apply": false,
  "sync_mode": "merge"
}

Calculate integrity.data_sha256 only after assembling the final data value. Serialize it as compact JSON, calculate the SHA-256 digest of those bytes, and do not mutate its values or key order before sending the request.

The dry-run result has dry_run: true, applied: false, publication_id (or null when it will be created), publication_slug, publication_action (create or update), authenticated_proxy_actor_id, verified_source_machine_id, bundle_source_machine_id, sync_mode, summary, and warnings. It makes no data changes.

When the review is acceptable, resend the same bundle with apply: true. merge writes bundle records and keeps target records not represented in the bundle. overwrite makes the bundle source-authoritative and removes target publication records absent from the bundle. An applied result includes all dry-run identity fields with dry_run: false and applied: true, plus imported. imported contains the per-section imported counts, settings_saved, sync_mode, and a removed map for records deleted by an overwrite.