cURL guide
Verify a key, submit complete capture events, search memory, and inspect upload streams from your terminal.
1. Prepare your terminal
Use cURL 7.76 or newer for --fail-with-body. Commands on this page use Bash syntax and run on macOS, Linux, WSL, or Git Bash. JSON files keep quoting predictable. Use a test account or authorized workspace: capture and upload commands create real data.
curl --version
export CONTEXARA_API_KEY="ctx_live_replace_with_your_key"Create an API key in the dashboard at /keys. Enable capture permission for writes and recall permission for searches. Keep the key on your server or in your secret manager; never expose it in browser JavaScript, a public repository, or logs. The key determines the authenticated account. A workspace ID selects an authorized destination; it does not grant access to another account.
Windows PowerShell users should invoke curl.exe, not the curl alias. Bash line continuations and $CONTEXARA_API_KEY do not translate directly to PowerShell. Here is an equivalent one-line verification command:
$env:CONTEXARA_API_KEY = "ctx_live_replace_with_your_key"
curl.exe --fail-with-body --silent --show-error -H "x-api-key: $env:CONTEXARA_API_KEY" https://api.contexara.com/verify2. Verify the key
curl --fail-with-body --silent --show-error \
--connect-timeout 10 --max-time 30 \
-H "x-api-key: $CONTEXARA_API_KEY" \
https://api.contexara.com/verify{"ok":true}Expect HTTP 200 for an active key and 401 for an invalid/inactive key. This does not verify capture/recall permission or quota; those checks run on the actual operation. --fail-with-body preserves an HTTP error body and returns a nonzero exit status for HTTP errors.
3. Search existing memories
Save the following as search.json. Then run the command below from the directory containing the file.
{
"query": "What deployment region did we choose?",
"limit": 5,
"scope": "general",
"deep": false,
"force_fast": false
}curl --fail-with-body --silent --show-error \
--connect-timeout 10 --max-time 30 \
-H "x-api-key: $CONTEXARA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @search.json \
https://retrieval.contexara.com/v1/memory/searchA successful response has results and timings_ms; results may be empty. See the REST reference for the full field definitions. Reading existing memory does not require a preceding capture command.
{"results":[],"timings_ms":{}}This is an illustrative empty response. Actual timing keys depend on the server path. Preserve memory ordering, and do not interpret rrf_score as a probability.
4. Capture a conversation turn
Save this complete body as capture.json. Before first delivery, replace event_id with a unique event identifier, session_id with your stable conversation identifier, and created_at with the event’s timezone-aware creation time. These example values are placeholders, not values to reuse for different writes.
{
"event_id": "evt_replace_with_a_new_unique_id",
"event_type": "conversation_turn",
"event_version": "1.0",
"capture_version": "my-app-0.1.0",
"schema_version": "1.0",
"turn_id": "turn_000001",
"tenant_id": "unset",
"user_id": "unset",
"session_id": "conversation_replace_with_your_stable_id",
"turn_number": 1,
"project_id": null,
"workspace_id": null,
"provider": "sdk",
"created_at": "2026-09-29T12:00:00Z",
"status": "complete",
"incomplete_reason": null,
"conversation": {
"user": {
"timestamp": null,
"content": "The deployment region is Mumbai."
},
"assistant": {
"timestamp": null,
"content": null
}
},
"metadata": {
"capture_client_version": "0.1.0",
"os": null,
"hook_event_names": []
}
}turn_number begins at 1. turn_id must be turn_ followed by the same number padded to at least six digits: turn_000001 for 1, turn_000042 for 42. Increment for each new turn within the same session. The server derives account identity from the key; send literal unset for tenant_id and user_id. Null assistant content is valid when there was no assistant reply.
curl --fail-with-body --silent --show-error \
--connect-timeout 10 --max-time 30 \
-D capture-headers.txt -o capture-response.json \
-H "x-api-key: $CONTEXARA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @capture.json \
https://api.contexara.com/ingestcat capture-headers.txt
cat capture-response.json{"ok":true,"turn_id":"turn_000001","status":"accepted"}Expect 202 accepted in queued deployments, or 200 processed in synchronous deployments. The acknowledgement does not return the locally chosen event_id or session_id; retain those in your application.
Generate identity once, replay unchanged
For a Bash walkthrough, Python’s standard library can stamp fresh IDs and a UTC timestamp into your capture.json template before its first delivery. Run this once for a new conversation, before sending. Do not run it again when retrying that event.
python3 - <<'PY'
import json
from datetime import datetime, timezone
from pathlib import Path
from uuid import uuid4
path = Path("capture.json")
event = json.loads(path.read_text())
event["event_id"] = str(uuid4())
event["session_id"] = str(uuid4())
event["created_at"] = datetime.now(timezone.utc).isoformat()
path.write_text(json.dumps(event, indent=2))
PYSearch a project or workspace
Use general for unassigned memories in the selected workspace, or project with an active project ID. Omit the workspace ID for your personal workspace; pass an authorized workspace ID for a team workspace. A project must belong to that workspace. Do not pass a project ID with general scope. all_active is outside the supported stable contract. Capture and recall destinations are separate: explicitly use the same intended project and workspace in both calls.
Replace the UUIDs below with an authorized active project and its workspace. Save as search.json and reuse the search command. For a personal project, omit workspace_id.
{
"query": "deployment region",
"scope": "project",
"project_id": "11111111-1111-4111-8111-111111111111",
"workspace_id": "22222222-2222-4222-8222-222222222222",
"limit": 10
}For capture, put the same intended project_id and workspace_id into capture.json before its first delivery. For uploads, the multipart workspace field is explicit_workspace_id.
Request Deep Recall
Set deep=true in search.json to request synthesis. Do not also set force_fast=true. A request can fall back to Fast Recall when plan or quota conditions do not permit synthesis. synthesis may be null even when Deep Recall ran.
{
"query": "Why did we choose Mumbai?",
"scope": "general",
"limit": 5,
"deep": true
}Check for the presence of synthesis/grounded_in_source_windows to distinguish a Deep Recall response, then check synthesis separately for a non-null answer. grounded_in_source_windows is a boolean. Deep Recall may incur generation cost before a timeout; retry conservatively. Its final results are not guaranteed to be capped by limit.
Upload a file
Create notes.md and run the command below. Let cURL generate the multipart Content-Type boundary; do not manually set Content-Type to application/json or a boundary-less multipart value.
curl --fail-with-body --silent --show-error --no-buffer \
--connect-timeout 10 --max-time 120 \
-H "x-api-key: $CONTEXARA_API_KEY" \
-H "Accept: text/event-stream" \
-F "file=@notes.md;type=text/markdown" \
-F "caption=Roadmap notes" \
-F "explain=false" \
https://api.contexara.com/uploadAdd -F "project_id=YOUR_PROJECT_UUID" and -F "explicit_workspace_id=YOUR_WORKSPACE_UUID" when targeting a project in a team workspace. Optional fields also include session_id and group_id; group_id groups image uploads. The explicit explain=false matters because the raw HTTP endpoint defaults to true.
| File type | Accepted MIME type | Maximum bytes |
|---|---|---|
| PNG / JPEG / WebP | image/png, image/jpeg, image/webp | 20,000,000 |
| application/pdf | 50,000,000 | |
| DOCX | application/vnd.openxmlformats-officedocument.wordprocessingml.document | 20,000,000 |
| PPTX | application/vnd.openxmlformats-officedocument.presentationml.presentation | 50,000,000 |
| TXT / Markdown | text/plain, text/markdown | 5,000,000 |
Read streaming responses correctly
The response is Server-Sent Events, not one JSON document. --no-buffer displays frames as they arrive. Metadata comes first, followed by zero or more chunk frames and a [DONE] marker. This is an illustrative non-image upload with explain=false:
data: {"content_hash":"example-hash","duplicate":false,"attachment_id":"example-attachment"}
data: [DONE]data: {"content_hash":"example-hash","duplicate":false,"attachment_id":"example-attachment"}
data: {"error":"Extraction failed"}
data: [DONE]HTTP 200 and a zero cURL exit code cannot detect an error inside an SSE frame. A script consuming uploads must parse frames and reject an error event or termination without [DONE]. --max-time may stop a long valid stream; choose a budget appropriate for your application and treat a timeout as an uncertain outcome.
Inspect HTTP failures and retry safely
| HTTP status | Meaning | Recovery |
|---|---|---|
| 400 | Blank query or invalid limit. | Correct the request. |
| 401 | Missing, invalid, or inactive API key. | Replace or activate the key. |
| 402 | Personal or team hard quota lock. | Review usage and allowance. |
| 403 | Permission, preference, or workspace access denied. | Check key scope, preferences, and membership. |
| 409 | Capture turn conflict in synchronous processing. | Investigate identity/payload; do not invent a retry ID. |
| 413 | Request or upload too large. | Reduce the payload or use an allowed file size. |
| 415 | Unsupported upload MIME type. | Use an allowed MIME type and file format. |
| 422 | Invalid fields, scope, project, or multipart data. | Inspect the error response and correct the payload. |
| 429 | Rate limit or upload allowance exhausted. | Honor Retry-After when present; otherwise inspect allowance. |
| 5xx | Server or upstream failure. | Bound retries according to operation and delivery certainty. |
To inspect status and body separately, save a response and print the HTTP status. This command intentionally omits --fail-with-body so your script can handle the status itself; cURL’s exit status still reports transport errors.
curl --silent --show-error --connect-timeout 10 --max-time 30 \
-D response-headers.txt -o response.json -w '%{http_code}\n' \
-H "x-api-key: $CONTEXARA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @search.json \
https://retrieval.contexara.com/v1/memory/searchFast Recall and verify are safe reads for bounded retries. On 429, respect Retry-After if present; without it, inspect quota instead of assuming a short delay will fix the request. Capture retries must replay the exact stored body. Avoid global --retry-all-errors for writes. Uploads have no request-level idempotency and should not be retried automatically. Do not expose keys in curl -v traces or shared logs.
Move from terminal to application
Use the Python or Node.js SDK when you want built-in validation, typed results, exact capture replay, operation-specific retries, and SSE parsing. If you implement your own HTTP client, follow the REST API reference for field limits, scope rules, and response parsing.