space ocr

API Docs

https://api.space-ocr.com

Introduction

The space ocr API combines structured OCR (extract typed fields from images) with MySpace (store and query the results). One spocr_* API key unlocks every REST endpoint and event webhook.

RESTful, JSON, and CORS-enabled. For batch and async flows, see Jobs / Webhooks.

Authentication

Every request must send Authorization: Bearer <API_KEY>. Issue and revoke keys in Developer → API Keys.

Keys are prefixed with spocr_. Revoke immediately if leaked.

Request
1
2
curl https://api.space-ocr.com/amount \
  -H "Authorization: Bearer YOUR_API_KEY"

Base URL

There is a single production base URL. Versioning is signalled via keys and event apiVersion.

1
2
3
4
5
# Production
https://api.space-ocr.com

# OpenAPI spec
https://api.space-ocr.com/openapi.json

Rate limits

60 req/min/key, 600 req/min/uid. Exceeded requests return HTTP 429 with a Retry-After header (seconds).

All responses include an X-Request-Id (req_xxx) header — include it when contacting support.

/create and /upload support an Idempotency-Key header. Repeated requests return the cached response for 24h with X-Idempotent-Replay: true.

Errors

4xx / 5xx errors share a common envelope. requestId helps support trace the issue.

1
2
3
4
5
6
7
8
9
10
{
  "error": {
    "code": "invalid_request",
    "message": "image is required",
    "requestId": "req_xxx"
  },
  "details": {
    /* optional, endpoint-specific context (e.g. /upload returns processable count) */
  }
}

HTTP status

200
Success
400
Malformed request / validation failed
401
Invalid or missing API key
402
Insufficient balance. /upload returns details.requested / processable / breakdown
404
Path not found / called outside api.space-ocr.com
429
Rate limited. Retry-After header gives wait seconds
500
Internal error
502
OCR engine error (auto-refunded)
POST/ocr/fieldsBearer¥10

Structured OCR

Extract typed fields from an image. Use templateId for built-in formats or fields for a custom schema.

Body parameters

imagestringrequired
Base64 string or image URL.
imageType"base64" | "url"required
Explicit type of image. The legacy name image_type still works (deprecated).
templateIdstringoptional
Built-in template id (see Templates). When set, fields and prompt are auto-applied. The legacy name template_id still works (deprecated).
fieldsarray<FieldSpec>optional
Extraction schema. Optional when templateId is set; fields takes precedence if both are given.
autoFieldsbooleanoptional
When true, the LLM proposes a schema if neither fields nor templateId is provided. The legacy name auto_fields still works (deprecated).
promptstringoptional
Free-form natural language instruction (optional).
Request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
curl -X POST https://api.space-ocr.com/ocr/fields \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://example.com/receipt.jpg",
    "imageType": "url",
    "fields": [
      { "name": "store_name", "type": "string",
        "description": "Store name" },
      { "name": "date",       "type": "string",
        "description": "Date" },
      { "name": "items",      "type": "array",
        "description": "Items",
        "children": [
          { "name": "name",  "type": "string" },
          { "name": "qty",   "type": "string" },
          { "name": "price", "type": "string" }
        ]
      },
      { "name": "total",      "type": "string",
        "description": "Total" }
    ]
  }'
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
  "status": "success",
  "data": {
    "store_name": "Supermarket ABC",
    "date": "2025-04-10",
    "items": [
      {
        "name": "Milk",
        "qty": "1",
        "price": "$1.99",
        "bbox":     { "xmin": 263, "ymin": 460, "xmax": 738, "ymax": 523 },
        "vertices": [{"x":263,"y":460},{"x":738,"y":460},{"x":738,"y":523},{"x":263,"y":523}],
        "match_ratio": 1.0,
        "bbox_source": "vision_symbol_match",
        "field_bboxes": {
          "name":  { "bbox": { "xmin": 263, "ymin": 460, "xmax": 503, "ymax": 492 }, "match_ratio": 1.0 },
          "qty":   { "bbox": { "xmin": 333, "ymin": 460, "xmax": 338, "ymax": 490 }, "match_ratio": 1.0 },
          "price": { "bbox": { "xmin": 693, "ymin": 460, "xmax": 738, "ymax": 488 }, "match_ratio": 1.0 }
        }
      }
    ],
    "total": "$4.94",
    "bbox":        { "xmin": 12, "ymin": 34, "xmax": 532, "ymax": 774 },
    "vertices":    [{"x":12,"y":34},{"x":532,"y":34},{"x":532,"y":774},{"x":12,"y":774}],
    "match_ratio": 0.97,
    "bbox_source": "vision_symbol_match",
    "field_bboxes": {
      "store_name": { "bbox": { "xmin": 14, "ymin": 36,  "xmax": 210, "ymax": 58  }, "match_ratio": 0.98 },
      "date":       { "bbox": { "xmin": 14, "ymin": 80,  "xmax": 180, "ymax": 102 }, "match_ratio": 1.0 },
      "total":      { "bbox": { "xmin": 380, "ymin": 720, "xmax": 530, "ymax": 742 }, "match_ratio": 1.0 }
    }
  }
}
GET/spaceBearer

List tree

List folders / sheets / memos in MySpace. Scope with path and depth.

Query parameters

pathstringoptional
Slash-separated. Folders by name; sheets by name (unique within parent) or by the uniqueKey returned from /create. Default "/".
depthinteger 1..10optional
Recursion depth. Default 1.
Request
1
2
curl https://api.space-ocr.com/space?path=/&depth=1 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
6
7
8
{
  "path": "/",
  "depth": 1,
  "items": [
    { "path": "/invoices", "name": "invoices", "type": "folder" },
    { "path": "/memo_2024", "name": "memo", "type": "memo", "uniqueKey": "..." }
  ]
}
GET/viewBearer

View item contents

Returns folder / sheet / memo / image contents. Sheets support free server-side where / sort / limit / offset / select.

Query parameters

pathstringrequired
Target path.
wherestring | string[]optional
Row filter (e.g. total>=40000 or vendor~ABC). Repeatable (AND). Operators: = != > >= < <= ~. Matches columns plus name / ocrStatus.
sortstring | string[]optional
Sort spec (e.g. total:desc or -invoice_date). Repeatable for tie-breaks.
selectstringoptional
Comma-separated columns to return (projection), e.g. vendor,total.
limitinteger 1..500optional
Max rows to return.
offsetintegeroptional
Rows to skip for pagination. Pair with response.nextOffset.
boxes"0" | "1" | "true" | "false"optional
Set 0 / false to drop field_bboxes / vertices for a lean payload.
Request
1
2
3
4
5
6
7
8
# Multiple where (AND) + sort + projection + pagination
curl "https://api.space-ocr.com/view?path=/invoices/sheet1\
&where=total>=10000\
&where=vendor~ABC\
&sort=-invoice_date\
&select=vendor,total,invoice_date\
&limit=20&offset=0" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// type=sheet
{
  "type": "sheet",
  "path": "/invoices/sheet1",
  "name": "sheet1",
  "columns": [ /* ... */ ],
  "total": 128,        // total rows in the sheet
  "matched": 12,       // rows that passed where
  "offset": 0,
  "limit": 20,
  "nextOffset": 20,    // next page / null when exhausted
  "rows": [
    {
      "rowKey": "img_abc",
      "name": "invoice_2025_04_10.jpg",
      "imageUrl": "https://...",
      "ocrStatus": "done",
      "values": { "vendor": "ABC Corp", "total": "12000", "invoice_date": "2025-04-10" }
    }
  ]
}

// type=memo
{ "type": "memo", "path": "...", "name": "todo", "text": "..." }

// type=img
{ "type": "img", "path": "...", "name": "...", "imageUrl": "...", "ocrStatus": "done" }
POST/createBearer

Create folder / sheet / memo

Create a folder, sheet, or memo under the parent path. Sheets can carry an OCR schema (columns) and prompt.

Body parameters

pathstringrequired
Parent folder path.
type"folder" | "sheet" | "memo"required
Kind of item to create.
namestringrequired
Display name.
textstringoptional
Memo body (type=memo only).
columnsarray<ColumnSpec>optional
Sheet OCR schema (type=sheet). Each item: { id?, name, type, description?, children? }.
promptstringoptional
Optional prompt that guides extraction for this sheet.
Request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# sheet
curl -X POST https://api.space-ocr.com/create \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "path": "/invoices",
    "type": "sheet",
    "name": "sheet1",
    "columns": [
      { "id": "amount", "name": "amount", "type": "string" },
      { "id": "date",   "name": "date",   "type": "string" }
    ],
    "prompt": "Extract amount and date from invoice"
  }'
Response
1
2
// sheet/memo は uniqueKey が path に組み込まれて返却される
{ "path": "/invoices/Kvho45OXMKw…", "type": "sheet", "uniqueKey": "Kvho45OXMKw…" }
POST/uploadBearer¥10 × N

Upload images

Upload one or more images into a sheet. multipart/form-data. Async by default (returns jobs, completion via webhook).

Form fields (multipart)

pathstringrequired
Target sheet path.
filesfile (repeatable)required
Image file(s). For multiple, repeat the files field.
waitbooleanoptional
Set true for sync mode (waits up to 60s). Suitable for single-file uploads.
Request
1
2
3
4
5
curl -X POST https://api.space-ocr.com/upload \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "path=/invoices/sheet1" \
  -F "files=@invoice1.jpg" \
  -F "files=@invoice2.jpg"
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// async (default)
{
  "path": "/invoices/sheet1",
  "jobs": [
    { "uniqueKey": "...", "originalName": "invoice1.jpg", "jobId": "job_...", "status": "pending" },
    { "uniqueKey": "...", "originalName": "invoice2.jpg", "jobId": "job_...", "status": "pending" }
  ]
}

// 402 — insufficient balance
{
  "error": { "code": "insufficient_balance", "message": "...", "requestId": "req_..." },
  "details": { "requested": 5, "processable": 3, "breakdown": [...] }
}
POST/editBearer

Edit sheet cell or memo

Update a sheet cell value or memo body. anyOf: (path, row, column, value) or (path, text).

Body parameters

pathstringrequired
Target sheet or memo path.
rowinteger | stringoptional
Integer index (negatives count from end) or rowKey string. Required for sheet edits.
columninteger | stringoptional
Integer column index or column name / id. Required for sheet edits.
valueanyoptional
New cell value. Required for sheet edits.
textstringoptional
New memo body. Required for memo edits.
Request
1
2
3
4
5
6
7
8
9
10
11
# sheet
curl -X POST https://api.space-ocr.com/edit \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path":"/invoices/sheet1","row":"img_abc","column":"amount","value":"12000"}'

# memo
curl -X POST https://api.space-ocr.com/edit \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path":"/todo","text":"new body"}'
Response
1
{ "ok": true, "patched": { "row": "img_abc", "column": "amount", "value": "12000" } }
POST/removeBearer

Delete (cascade)

Delete a folder / sheet / memo / image. Folder deletes cascade through metadata, flat entries, and Storage.

Body parameters

pathstringrequired
Path of the item to delete.
Request
1
2
3
4
curl -X POST https://api.space-ocr.com/remove \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path":"/invoices/2024"}'
Response
1
{ "ok": true }
GET/jobs/{jobId}Bearer

Poll OCR job status

Check the status of a jobId returned by POST /upload (async). Use this if you don't consume webhooks.

Path parameters

jobIdstringrequired
From the /upload response (jobs[].jobId).
Request
1
2
curl https://api.space-ocr.com/jobs/job_xxx \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
6
7
{
  "jobId": "job_xxx",
  "status": "done",
  "uniqueKey": "img_abc",
  "path": "/invoices/sheet1/img_abc",
  "result": { "amount": "12000", "date": "2025-04-10" }
}
GET/amountBearer

Account balance and quota

Returns the current balance and available quota.

Request
1
2
curl https://api.space-ocr.com/amount \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
{
  "balance": 1240,
  "currency": "JPY",
  "freeScansRemaining": 0
}
GET/health

Service health

Public health check (no auth required).

Request
1
curl https://api.space-ocr.com/health
Response
1
{ "status": "ok", "version": "v1", "time": 1716700000000 }

Overview

Register one space-wide Webhook URL and every event is delivered with an HMAC signature. Configure via Developer → Webhooks or the management endpoints below.

Events

All events share the same envelope: event / deliveryId / occurredAt / apiVersion / data.

item.createdeventoptional
Folder/sheet/memo created via /create
upload.receivedeventoptional
Image received via /upload
ocr.completedeventoptional
OCR finished. data.result holds the extraction
ocr.failedeventoptional
OCR failed (auto-refunded)
webhook.testeventoptional
Manual test triggered by /webhook/test

Payload example — ocr.completed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "event": "ocr.completed",
  "deliveryId": "dlv_xxx",
  "occurredAt": 1716700000000,
  "apiVersion": "v1",
  "data": {
    "uid": "...",
    "path": "/invoices/sheet1/img_abc",
    "parentPath": "/invoices/sheet1",
    "uniqueKey": "img_abc",
    "sheetRef": "sht_xxx",
    "mode": "sheet",
    "result": { "amount": "12000", "date": "2025-04-10", "field_bboxes": { /* ... */ } }
  }
}

Delivery headers

Receivers see the headers below. Signature and Timestamp are needed for verification.

1
2
3
4
5
X-Spaceocr-Signature: t=<unix_ms>,v1=<hex>
X-Spaceocr-Timestamp: <unix_ms>
X-Spaceocr-Event: ocr.completed
X-Spaceocr-Delivery: dlv_<id>
Content-Type: application/json

Signature verification

X-Spaceocr-Signature is `t=<unix_ms>,v1=<hex>`. Canonical string: `${t}.${rawBody}`. Algorithm: HMAC-SHA256. Reject if timestamp drifts more than 5 minutes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import crypto from "crypto";

export function verify(secret, headers, rawBody) {
  const sig = headers["x-spaceocr-signature"] || "";
  const m = sig.match(/^t=(\d+),v1=([a-f0-9]+)$/);
  if (!m) return false;
  const [, t, v1] = m;
  if (Math.abs(Date.now() - Number(t)) > 5 * 60 * 1000) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(v1, "hex"),
  );
}

Retry policy

Non-2xx triggers exponential backoff (1m → 5m → 30m → 2h → 6h), max 5 attempts. 5xx / 408 / 429 / timeout retry; other 4xx are dead. Delivery logs retained 30 days.

For manual redelivery, see POST /webhooks/deliveries/{deliveryId}/redeliver below.
GET/webhookBearer

Get webhook configuration

Returns the currently configured space-wide webhook URL and state.

Request
1
2
curl https://api.space-ocr.com/webhook \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
6
{
  "url": "https://example.com/hooks/space-ocr",
  "active": true,
  "createdAt": 1716700000000,
  "updatedAt": 1716700000000
}
PUT/webhookBearer

Create or update webhook

Register or update the space-wide webhook URL. Use rotateSecret to re-issue the signing secret.

Body parameters

urlstring (uri)required
Delivery URL.
activebooleanoptional
Enable delivery (default true).
rotateSecretbooleanoptional
When true, rotate the signing secret and return the new value.
Request
1
2
3
4
curl -X PUT https://api.space-ocr.com/webhook \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/space-ocr","active":true}'
Response
1
2
3
4
5
{
  "url": "https://example.com/hooks/space-ocr",
  "active": true,
  "secret": "whsec_..." // rotateSecret 일 때만 평문으로 한 번만 받아요
}
DELETE/webhookBearer

Remove webhook

Remove the configured webhook. No further events will be delivered.

Request
1
2
curl -X DELETE https://api.space-ocr.com/webhook \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
{ "ok": true }
POST/webhook/testBearer

Send a test event

Immediately send a webhook.test event to the configured URL. Useful for verifying the receiver.

Request
1
2
curl -X POST https://api.space-ocr.com/webhook/test \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
{ "ok": true, "deliveryId": "dlv_xxx" }
GET/webhooks/deliveriesBearer

List recent deliveries

Returns recent webhook delivery logs. Useful for debugging.

Request
1
2
curl https://api.space-ocr.com/webhooks/deliveries \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
6
7
8
9
10
11
12
{
  "deliveries": [
    {
      "deliveryId": "dlv_xxx",
      "event": "ocr.completed",
      "occurredAt": 1716700000000,
      "status": "delivered",
      "responseStatus": 200,
      "attempts": 1
    }
  ]
}
GET/webhooks/deliveries/{deliveryId}Bearer

Get delivery detail

Returns the full payload and attempt history for a delivery.

Path parameters

deliveryIdstringrequired
From /webhooks/deliveries.
Request
1
2
curl https://api.space-ocr.com/webhooks/deliveries/dlv_xxx \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
2
3
4
5
6
7
8
9
{
  "deliveryId": "dlv_xxx",
  "event": "ocr.completed",
  "occurredAt": 1716700000000,
  "payload": { /* full event body */ },
  "attempts": [
    { "at": 1716700000000, "responseStatus": 200, "ok": true }
  ]
}
POST/webhooks/deliveries/{deliveryId}/redeliverBearer

Manually redeliver

Manually redeliver a failed webhook.

Path parameters

deliveryIdstringrequired
deliveryId to redeliver.
Request
1
2
curl -X POST https://api.space-ocr.com/webhooks/deliveries/dlv_xxx/redeliver \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
1
{ "ok": true, "newDeliveryId": "dlv_yyy" }

Built-in templates

13 built-in templates are available. Pass any of these IDs as templateId on POST /ocr/fields and the schema and prompt are applied automatically.

Contact
Trade Documents
Payment & Finance
Identity Documents
Government & Public Documents