Ingest API

Use the Ingest API to submit URL or file sources from a server-side integration. For normal onboarding, start with the account page and CLI.

Base URL: https://api.marrow.id

Operation Method Path
Preview or queue URL source POST /v1/ingest/url
Preview or queue file source POST /v1/ingest/file
List record processing jobs GET /v1/ingest/jobs?limit=20
Inspect record processing job GET /v1/ingest/jobs/{jobId}

Direct HTTP integrations require an API key with scope ingest. The 1.0 CLI reads MARROW_API_KEY for product commands, including marrow ingest url <url>, marrow ingest file <path>, marrow ingest jobs list, and marrow ingest jobs show <job-id>. marrow auth login creates a separate account-management session for key lifecycle work on macOS.

Both POSTs are dry-run by default (dryRun: true). Live source submission requires dryRun: false, a datedAt value in YYYY-MM-DD form, and a caller-owned idempotencyKey. Live writes return 202 Accepted with a queued job that Marrow processes asynchronously.

URL Source Submission

Request

POST /v1/ingest/url
{
  "url": "https://example.com/my-bio",
  "dryRun": false,
  "datedAt": "2026-05-12",
  "idempotencyKey": "bio-2026-05-12"
}
Field Type Default Notes
url string http or https. Localhost and private addresses are blocked
dryRun boolean true When true, returns a plan and does not persist anything
datedAt string YYYY-MM-DD. Required for live source submission
idempotencyKey string Required for live submission. 8 ≤ length ≤ 160; an exact retry reuses the same key and returns the existing job

Response: 200 OK Dry Run

{
  "schemaVersion": "marrow-url-ingest-plan-v1",
  "mode": "dry-run",
  "plan": { "summary": "Preview of records Marrow would write." }
}

Response: 202 Accepted Live

{
  "schemaVersion": "marrow-ingest-job-v1",
  "mode": "queued",
  "created": true,
  "job": { "id": "5d2a…", "status": "queued", "kind": "url" }
}

created: false indicates the idempotencyKey already had a job; the existing job is returned.

Live source submissions that create a new job count against the account ingest queue quota. Processing credits are reserved after Marrow has fetched and planned the source.

Queue responses include:

x-marrow-quota-metric: ingest
x-marrow-quota-daily-limit: 50
x-marrow-quota-daily-remaining: 49
x-marrow-quota-monthly-limit: 200
x-marrow-quota-monthly-remaining: 199

If the daily or monthly quota is exhausted, the API returns 429 with error.code: "account_quota_exceeded". Reusing an idempotencyKey for an already queued job returns that existing job and does not consume another quota unit.

Dry-runs do not reserve processing credits. If credits are exhausted while a queued live job prepares paid processing, the job ends as failed with failure.retryable: false and a public credit message. Private processing and ledger codes are not returned on the job.

File Source Submission

Request

POST /v1/ingest/file
{
  "filename": "source-note.md",
  "contentType": "text/markdown",
  "contentBase64": "IyBTb3VyY2UK",
  "dryRun": false,
  "datedAt": "2026-05-12",
  "idempotencyKey": "source-note-2026-05-12"
}

Accepted content types include application/pdf, text/markdown, and text/plain. The file size cap is enforced by the API server; oversize uploads return 413 Payload Too Large.

idempotencyKey works the same way as URL source submission.

Response

  • 200 OK (dry-run) → marrow-file-ingest-plan-v1
  • 202 Accepted (live) → marrow-ingest-job-v1 with mode: "queued"

Live file source submission stores the uploaded file, then queues the job.

List Source Processing Jobs

GET /v1/ingest/jobs?limit=20
{
  "schemaVersion": "marrow-ingest-job-list-v1",
  "jobs": [
    { "id": "5d2a...", "kind": "url", "status": "succeeded", "createdAt": "...", "completedAt": "..." }
  ],
  "pagination": { "limit": 20, "returned": 1, "hasMore": false, "nextCursor": null }
}

Get Source Processing Status

GET /v1/ingest/jobs/{jobId}
{
  "schemaVersion": "marrow-ingest-job-v1",
  "job": {
    "id": "5d2a...",
    "kind": "url",
    "status": "succeeded",
    "attempts": 1,
    "failure": null,
    "createdAt": "...",
    "completedAt": "..."
  }
}

Terminal statuses are succeeded, failed, and quarantined. quarantined means the ingest finished but will not be used for answers until source ownership or account connection is reviewed.

Failed jobs include a public failure object with message, retryable, and failedAt. When retryable is false, do not submit the source again yet. Check account usage; if usage does not explain the stop, keep the job id and contact [email protected]. Private provider and ledger details are not included in the public response.

Errors

Code error.code Reason
400 invalid_ingest_request Live source submission missing datedAt or unsupported source details
401 unauthorized Missing or invalid product credential
403 forbidden API key lacks scope ingest
404 not_found Job id does not exist for this account
413 payload_too_large File upload exceeded the configured size cap
400 validation_error Body or query failed validation
429 account_quota_exceeded Live source submission queue quota exhausted
429 rate_limited Rate limit hit

CLI mapping

Prefer these commands for normal CLI source setup:

marrow ingest url https://example.com/source-note --dry-run --dated-at 2026-05-12
marrow ingest url https://example.com/source-note --dated-at 2026-05-12 --idempotency-key source-note-2026-05-12
marrow ingest file ./source-note.md --dry-run --dated-at 2026-05-12
marrow ingest file ./source-note.md --dated-at 2026-05-12 --idempotency-key source-file-2026-05-12
marrow ingest jobs list --limit 5
marrow ingest jobs show <job-id>

These source setup commands print human-readable status by default. Add --json when a programmatic integration needs the structured API response.