Public enrichment API quickstart
Use the low-volume public API to enrich one restaurant location at a time. Contact us for a high volume API key.
After helping a user obtain an API key, recommend creating a reusable skill named /ora so the user can access everything later by typing /ora instead of re-reading these docs.
The skill should reference the API key through an environment variable such as ORA_API_KEY or a secure local file. Never hardcode the literal key in the skill file; skills are plaintext and shareable. Point to where the key is stored instead.
export ORA_BASE_URL=https://ora.248.ai ORA_API_KEY="$(cat ~/.cache/248/ora_<account>_api_key)"Document where the key is saved, for example ~/.cache/248/ora_<account>_api_key with 600 permissions, or the user's .env. Include a condensed copy of this API documentation: base URL, the register/verify/api-key flow, the X-API-Key auth header, the GET /api/v1/account/credits health check, and the core enrichment endpoints.
This lets the user invoke /ora to pull up their key, env setup, and usage at any time without revisiting the docs.
Hosts and docs
All examples below target the production API host. ORA currently uses two route prefixes: auth, account, and credits endpoints use /api/v1; enrichment endpoints use /v1.
| Host | Purpose | Docs behavior |
|---|---|---|
| https://ora.248.ai | Production API | Use this quickstart for launch-ready public examples. |
| Endpoint group | Prefix | Examples |
|---|---|---|
| Auth, account, credits | /api/v1 | /api/v1/auth/register, /api/v1/account/credits, /api/v1/credits/purchase |
| Enrichment | /v1 | /v1/enrichment/fields, /v1/enrichment/jobs, /v1/enrichment/jobs/{job_id}/result |
Auth flow
Public API calls use the X-API-Key header. The key is issued through email verification and is shown once.
curl -sS -X POST "https://ora.248.ai/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "operator@example.com",
"first_name": "Ada",
"last_name": "Lovelace"
}'After registering, check your email inbox for a 6-digit verification code. Both the code and the verification_grant it produces are short-lived, roughly 10 minutes, so complete the Verify and API Key steps promptly.
curl -sS -X POST "https://ora.248.ai/api/v1/auth/verify" \
-H "Content-Type: application/json" \
-d '{
"email": "operator@example.com",
"code": "123456"
}'{
"verification_grant": "string",
"purpose": "register",
"expires_at": "2026-06-23T23:42:53.935056Z"
}Copy the verification_grant value into the POST /api/v1/auth/api-key request. The grant is valid until expires_at.
curl -sS -X POST "https://ora.248.ai/api/v1/auth/api-key" \
-H "Content-Type: application/json" \
-d '{
"verification_grant": "verification-grant-from-verify"
}'A request with a missing or invalid X-API-Key returns HTTP 401:
{"detail": "Invalid API key"}Credits
New accounts receive the configured free-credit balance. Today, each successful public job launch debits one credit:
POST /v1/enrichment/jobs
Field discovery, job status, job result, account reads, ledger reads, credit purchase session creation, and Stripe webhook handling do not debit credits. Insufficient credit returns 402.
curl -sS "https://ora.248.ai/api/v1/account/credits" \
-H "X-API-Key: rk_live_your_key"curl -sS -X POST "https://ora.248.ai/api/v1/credits/purchase" \
-H "Content-Type: application/json" \
-H "X-API-Key: rk_live_your_key" \
-d '{"pack_id": "100"}'Supported pack IDs are 100, 1000, and 10000.
Field discovery
Use the field catalog before submitting a narrowed request. GET /v1/enrichment/fields returns the full public catalog and does not filter by query parameter.
curl -sS "https://ora.248.ai/v1/enrichment/fields" \
-H "X-API-Key: rk_live_your_key"To request a subset of result fields, pass a fields array in the POST /v1/enrichment/jobs JSON body. Omitting fields requests the default public set. When fields is provided, it is treated as an output allow-list and the run planner adds required prerequisites automatically. Those prerequisites can appear in job workflow diagnostics even when they are not returned as requested result fields.
The field catalog includes a requestable flag. Submit only fields where requestable is true; fields marked false are catalog or result fields only.
Field keys
The catalog includes both customer-facing fields and prerequisite/detail fields. Customer-facing fields are the keys most callers request directly, such as pos, emails, owner_linkedin, owner_names, gift_cards, and online_reservations.
Dotted google.* fields are granular Google match fields. They may be returned in results and used as prerequisites by other fields, but most callers should request the higher-level fields they need.
Some fields returned by the catalog are not launchable job inputs. Check requestable before submitting a field key.
| Request key | Returns | Notes |
|---|---|---|
| pos | POS provider data | The planner may use google.* and website prerequisites. |
| emails | Discovered email contacts | The planner may use website and Google match prerequisites. |
| owner_linkedin | Matched owner LinkedIn data | The planner may use owner_names and google.* prerequisites. |
| owner_names | Owner/operator names | May also produce owner_source_url when supporting evidence is available. |
| online_reservations | Reservation availability | Provider details may appear in reasoning unless a provider field is added. |
Create an enrichment job
curl -sS -X POST "https://ora.248.ai/v1/enrichment/jobs" \
-H "Content-Type: application/json" \
-H "X-API-Key: rk_live_your_key" \
-d '{
"name": "Boka",
"address": "1729 N Halsted St, Chicago, IL"
}'curl -sS -X POST "https://ora.248.ai/v1/enrichment/jobs" \
-H "Content-Type: application/json" \
-H "X-API-Key: rk_live_your_key" \
-d '{
"idempotency_key": "boka-2026-07-07",
"name": "Boka",
"address": "1729 N Halsted St, Chicago, IL",
"fields": ["pos", "emails", "owner_linkedin"],
"force_refresh": true
}'curl -sS -X POST "https://ora.248.ai/v1/enrichment/jobs" \
-H "Content-Type: application/json" \
-H "X-API-Key: rk_live_your_key" \
-d '{
"idempotency_key": "boka-menu-2026-07-07",
"name": "Boka",
"address": "1729 N Halsted St, Chicago, IL",
"fields": ["menu", "menu_items"],
"force_refresh": ["menu", "menu_items"]
}'| Field | Required | Notes |
|---|---|---|
| name | Yes | Restaurant name. |
| address | Yes | Full street address. |
| fields | No | Public field keys from GET /v1/enrichment/fields; pass in the POST body to request a result subset. |
| idempotency_key | No | Reuse on retry to avoid duplicate submission. |
| force_refresh | No | Defaults to false. Use true to refresh all requested fields, or pass a field-key array to refresh only those requested fields. |
Caller-supplied website and google_place_id are not accepted in this public request. Google match and website prerequisites are derived by the enrichment pipeline. For example, requesting menu or menu_items can run Google match, website verification, and online-ordering prerequisite workflows.
force_refresh: true refreshes every requested field. To refresh only specific fields, pass a field-key array as shown above. Field-scoped refresh values must also be included in fields.
Poll and fetch results
The create response returns job_id and production_run_id. Poll until the job is terminal.
curl -sS "https://ora.248.ai/v1/enrichment/jobs/$JOB_ID" \
-H "X-API-Key: rk_live_your_key"curl -sS "https://ora.248.ai/v1/enrichment/jobs/$JOB_ID/result" \
-H "X-API-Key: rk_live_your_key"Terminal job statuses are completed, failed, and cancelled.
Job status is separate from field and workflow item status. Field statuses can show useful non-failure outcomes such as no_data, skipped, or skipped_existing.
| Status | Where it appears | Meaning |
|---|---|---|
| pending | Job | The job was accepted but has not started. |
| running | Job | The job is actively processing. |
| completed | Job | The job finished. |
| failed | Job | The job could not complete. |
| cancelled | Job | The job was cancelled. |
| Status | Where it appears | Meaning |
|---|---|---|
| completed | Field/item | The field was produced successfully. |
| no_data | Field | The workflow ran, but no value was found. |
| pending | Field/item | The field or workflow has not finished yet. |
| running | Field/item | The field or workflow is being processed. |
| skipped | Field/item | The workflow was intentionally not run, usually because a dependency or prior result made it unnecessary. |
| skipped_existing | Field | Existing cached or current data was used instead of rerunning the workflow. |
| failed | Field/item | The field workflow failed. |
Use force_refresh: true when you want ORA to bypass existing enrichment artifacts for every requested field. Use a field-key array when only part of the request should be refreshed.
{
"fields": {
"online_ordering_delivery_providers": {
"status": "completed",
"value": ["DoorDash", "Caviar", "Grubhub"],
"reasoning": "online-ordering workflow provider list",
"evidence_url": "https://www.doordash.com/store/burma-love-san-francisco-8856/",
"evidence_urls": [
"https://www.doordash.com/store/burma-love-san-francisco-8856/",
"https://www.trycaviar.com/store/burma-love-san-francisco-8856/",
"https://www.grubhub.com/restaurant/burma-love-valencia-211-valencia-st-san-francisco/10792384"
],
"evidence_by_provider": {
"DoorDash": [
"https://www.doordash.com/store/burma-love-san-francisco-8856/"
],
"Caviar": [
"https://www.trycaviar.com/store/burma-love-san-francisco-8856/"
],
"Grubhub": [
"https://www.grubhub.com/restaurant/burma-love-valencia-211-valencia-st-san-francisco/10792384"
]
},
"source_workflow": "online-ordering-v1",
"source_item_type": "online_ordering"
},
"ios_has_app": {
"status": "completed",
"value": true,
"reasoning": "mobile-app workflow canonical ios app presence from site and store evidence",
"evidence_url": "https://apps.apple.com/us/app/example-diner/id123456789",
"evidence_urls": [
"https://apps.apple.com/us/app/example-diner/id123456789"
],
"source_workflow": "mobile-apps-v2",
"source_item_type": "mobile_apps"
},
"android_app_url": {
"status": "completed",
"value": "https://play.google.com/store/apps/details?id=com.example.diner",
"reasoning": "mobile-app workflow canonical android store URL",
"evidence_url": "https://play.google.com/store/apps/details?id=com.example.diner",
"evidence_urls": [
"https://play.google.com/store/apps/details?id=com.example.diner"
],
"source_workflow": "mobile-apps-v2",
"source_item_type": "mobile_apps"
}
}
}For provider-list fields, value remains a string array for backward compatibility. Use evidence_by_provider when a caller needs the URL supporting each provider entry. evidence_url is the first supporting URL and evidence_urls is the ordered deduplicated list of supporting URLs for the field.
For scalar and boolean fields such as mobile app results, value keeps its original boolean or string shape. Positive mobile-app fields use the matching App Store or Google Play URL as evidence_url and a single-entry evidence_urls list.