SDKs
Three first-party clients: Python, TypeScript, and Go. They all wrap the same HTTP API and handle auth, idempotency keys, and retries on transient failures for you. Pick whichever fits your stack.
For raw HTTP usage, see HTTP API reference. For the command line, see CLI (oc).
1. Install.
# cURL is built into macOS and most Linux distros.
curl --versionpip install originchain
# Async client available in the same package:
# from originchain import AsyncOriginChainnpm install @originchain/sdk
# Also works with pnpm / yarn / bun. ESM + CJS bundles + .d.ts shipped.
# Node >= 18 has fetch built in.go get github.com/originchain/sdk-go
// Zero third-party deps. crypto/rand UUIDv4 Idempotency-Key on every
// mutating call - retries are safe by default. 2. Connect.
Three environment variables: endpoint URL, bearer token, tenant ID. Save them once per shell session.
# cURL has no client - every call carries the bearer header.
export OC_HOST=acme.ap-south-1.db.originchain.ai
export OC_TENANT=acme
export OC_TOKEN=oc_live_xxxxxxxxxxxxxxxx
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
"https://$OC_HOST/health" \
-H "Authorization: Bearer $OC_TOKEN"
# → HTTP 200 if everything is wired up.import os
from originchain import OriginChain
db = OriginChain(
base_url=f"https://{os.environ['OC_HOST']}",
bearer=os.environ["OC_TOKEN"],
tenant=os.environ["OC_TENANT"],
)import { OriginChainClient } from "@originchain/sdk";
const db = new OriginChainClient({
baseUrl: `https://${process.env.OC_HOST}`,
bearer: process.env.OC_TOKEN!,
});import (
"context"
"os"
"github.com/originchain/sdk-go"
)
var (
ctx = context.Background()
db = originchain.NewClient(originchain.Config{
BaseURL: "https://" + os.Getenv("OC_HOST"),
Bearer: os.Getenv("OC_TOKEN"),
})
)
The Python SDK also has OriginChain.from_env() which reads OC_BASE_URL, OC_BEARER, OC_TENANT directly.
3. The surface.
The same operations are exposed in every SDK; only the spelling differs. Below is the most-common subset - schemas, rows, SQL, vector, full-text, graph, and natural language.
# Schemas
db.schemas.list()
db.schemas.get("shop.orders")
db.schemas.register(open("orders.toml").read())
# Rows (single + bulk)
db.rows.put("shop.orders", { "id": "o-1", "status": "paid" })
db.rows.get("shop.orders", "o-1")
db.rows.put_batch("shop.orders", rows_iterable, chunk=1000)
# SQL
result = db.sql("SELECT * FROM shop.orders WHERE status = 'paid' LIMIT 10")
# Vector
db.vector.put("shop.products", "sku-1", embedding_768d)
hits = db.vector_topk("shop.products", query=q768, k=10, dim=768)
# Full-text
db.fts.index("shop.products", "description", doc_id="sku-1", text="...")
hits = db.fts.search("shop.products", "description", q="...", mode="bm25", k=10)
# Graph
neighbors = db.graph.neighbors("shop.orders", rel="customer", pk="c-1")
# Natural language
result = db.ask("top 5 customers by revenue", schemas=["shop.orders"])// SQL
const result = await db.sql("SELECT * FROM shop.orders WHERE status = 'paid' LIMIT 10");
// Vector
await db.vectorPut("shop.products", { id: "sku-1", embedding: emb768, dim: 768 });
const hits = await db.vectorTopk("shop.products", { query: q768, k: 10, dim: 768 });
// Full-text
await db.ftsIndex("shop.products", "description", { doc_id: "sku-1", text: "..." });
const fHits = await db.ftsSearch("shop.products", "description", { q: "...", mode: "bm25", k: 10 });
// Graph
const ns = await db.graph.neighbors("shop.orders", { rel: "customer", pk: "c-1" });
// Natural language
const ask = await db.ask("top 5 customers by revenue", { schemas: ["shop.orders"] });
// Schema register
await db.registerSchema(tomlText);
// Row reads / writes don't have helpers yet - use fetch with the bearer header.
// See /docs/insert and /docs/quickstart for examples.// SQL
result, _ := db.SQL(ctx, "SELECT * FROM shop.orders WHERE status = 'paid' LIMIT 10")
// Vector
db.VectorPut(ctx, "shop.products", originchain.VectorPutRequest{
ID: "sku-1", Embedding: emb768, Dim: 768,
})
hits, _ := db.VectorTopK(ctx, "shop.products", originchain.VectorTopKRequest{
Query: q768, K: 10, Dim: 768,
})
// Full-text
db.FTSIndex(ctx, "shop.products", "description", originchain.FTSIndexRequest{
DocID: "sku-1", Text: "...",
})
fHits, _ := db.FTSSearch(ctx, "shop.products", "description", originchain.FTSSearchRequest{
Q: "...", Mode: "bm25", K: 10,
})
// Graph
ns, _ := db.Graph().Neighbors(ctx, "shop.orders", originchain.NeighborsRequest{
Rel: "customer", PK: "c-1",
})
// Natural language
ask, _ := db.Ask(ctx, "top 5 customers by revenue")
// Row reads / writes via net/http (helpers ship in the next release). 4. What each SDK covers today.
Python is the most complete client. TypeScript and Go cover SQL, vector, full-text, graph, and ask - row reads and writes go through raw fetch / net/http until the helpers ship in the next release.
| Operation | Python | TypeScript | Go |
|---|---|---|---|
| Register schema | yes | yes | use http |
| Row reads + writes | yes | use fetch | use http |
| Bulk row writes | yes | use fetch | use http |
| SQL | yes | yes | yes |
| Vector put / topk | yes | yes | yes |
| Full-text index / search | yes | yes | yes |
| Graph algorithms | yes | yes | yes |
| Natural language (ask) | yes | yes | yes |
| Async client | AsyncOriginChain | native | native |
5. Error handling.
Each SDK raises typed errors so you can switch on the failure mode rather than parsing strings. See Error reference for the complete code catalog.
from originchain import (
OCAuthError, # 401, 403
OCValidationError, # 400
OCNotFoundError, # 404
OCRateLimitedError, # 429 (also exposes .retry_after)
OCServerError, # 5xx
)
try:
db.rows.put("shop.orders", { "id": "o-1", "amount_cents": "not-a-number" })
except OCValidationError as e:
print(e.status, e.body)import { ApiError, OCAddonRequiredError } from "@originchain/sdk";
try {
await db.sql("SELECT * FROM shop.orders WHERE bad_col = 1");
} catch (e) {
if (e instanceof ApiError) {
console.error(e.status, e.code, e.message);
}
}result, err := db.SQL(ctx, "SELECT * FROM shop.orders WHERE bad_col = 1")
if err != nil {
var apiErr *originchain.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.Status, apiErr.Code, apiErr.Message)
}
} 6. Retries + idempotency.
Every SDK attaches a fresh Idempotency-Key header to every mutating call (POST / PUT / PATCH / DELETE). If a network blip causes a retry, the server returns the original result without re-applying the write.
The retry policy is the same in every SDK: 429 honours Retry-After; 500/502/503/504 use exponential backoff (0.25s, 0.5s, 1s, 2s, capped at 4s) up to 3 retries by default.
For cross-process retries (e.g., a queue worker re-attempting a job after a crash), generate a stable idempotency key from the job's ID and pass it explicitly. The default fresh-per-call UUID only protects retries within one HTTP request lifecycle.