Getting Started

Extract structured data from documents programmatically. There are three ways in: an official SDK (recommended — install it and process a document in a single call, then search and chat over your results), raw HTTP against the REST API from any language, or a drop-in chat UI you embed in your own app. Responses are structured JSON with extracted fields, coordinates, and confidence scores.

Base URLhttps://api.gemina.co
AuthenticationX-API-Key: your-api-key
Recommended

Install one package, then process, search & chat in native code — Document Intelligence included. The fastest path for most integrations.

Install

Add the official Gemina SDK to your project:

npm i @gemina/sdk

Process a document in one call

Authenticate with your API key and extract structured data — the SDK submits the document and polls for the result for you, so there's no upload-then-poll loop to write:

import { readFile } from "node:fs/promises";
import { GeminaClient } from "@gemina/sdk";

const client = new GeminaClient(process.env.GEMINA_API_KEY!);

// Node: wrap a Buffer in a Blob. In the browser, pass a File from an
// <input type="file"> directly.
const buf = await readFile("./invoice.png");
const result = await client.processDocument(
  new Blob([buf], { type: "image/png" }),
  ["invoice_headers"],
);

const values = result.data?.extractions?.[0]?.values;
console.log("supplier:", values?.vendorName?.value);
console.log("total:", values?.totalAmount?.value);
console.log("date:", values?.invoiceDate?.value);

Search & analyze your documents

Query everything you've processed with structured filters, semantic search, or both — and compute exact, database-backed aggregations without re-reading a document:

const { items, meta } = await client.retrieval.retrievalQuery({
  retrievalQueryInDTO: {
    text: "cleaning services invoices from August",
    filters: { totalAmountMin: 1000, currency: "ILS" },
    limit: 10,
  },
});

for (const item of items ?? []) {
  console.log(item.vendorName, item.totalAmount, item.issueDate, item.documentId);
}
console.log(`${meta.count} matches (mode: ${meta.mode})`);

const { rows } = await client.retrieval.retrievalAggregate({
  retrievalAggregateInDTO: {
    metrics: [{ op: "sum", field: "total_amount" }, { op: "count" }],
    groupBy: ["vendor_name"],
  },
});

for (const row of rows ?? []) {
  console.log(row.group, row.values);
}

Chat with your documents

Ask in natural language and get grounded answers with citations. Follow-up questions keep their conversation context:

const reply = await client.chat.chatQuery({
  chatQueryInDTO: { message: "How much did we spend on cleaning in 2020?" },
});

console.log(reply.answer);
console.log("confident:", reply.confident);
console.log("citations:", reply.citations);

const chat = client.conversation();
await chat.send("How much did we spend on cleaning in 2020?");
const follow = await chat.send("And which vendor was most expensive?"); // remembers 2020 / cleaning
console.log(follow.answer, "· session:", chat.sessionId);

await chat.delete(); // end it server-side (or chat.reset() to just forget it locally)

Browser-safe session tokens

Exchange your API key server-side for a short-lived, scoped token so a browser can search and chat without ever seeing the key:

// Server-side (holds the API key)
const session = await client.sessions.mintRetrievalToken({
  sessionTokenInDTO: { endUserId: "user-42", ttlSeconds: 900 },
});
// -> { token, expiresAt, expiresIn, tokenType }

// Browser (token only)
import { GeminaClient } from "@gemina/sdk";
const browserClient = GeminaClient.withSessionToken(session.token);
const results = await browserClient.retrieval.retrievalQuery({
  retrievalQueryInDTO: { text: "last month's invoices" },
});

Error handling

Typed errors separate a terminal processing failure from a still-processing timeout you can resume:

import { GeminaProcessingError, ResponseError } from "@gemina/sdk";

try {
  const result = await client.processDocument(file, ["invoice_headers"]);
} catch (err) {
  if (err instanceof GeminaProcessingError) {
    console.error("processing failed:", err.result.errors);
  } else if (err instanceof ResponseError) {
    console.error("HTTP error:", err.response.status);
  } else {
    throw err;
  }
}

No SDK, or on an unsupported language? Every endpoint is a plain REST call. Search, aggregate & chat over your processed documents is documented right here in curl; for the upload-and-extract quick start in your language, open the picker below. Response shapes are in Response Fields.

With Document Intelligence (opt-in per account), every successful extraction is indexed into a searchable layer — query your whole collection with exact filters, natural language, or both, and compute exact totals without re-reading a single document. Every SDK wraps this — see the “Search & analyze”, “Chat”, and “Session tokens” tabs above; the requests below are the underlying REST API.

Search your documents

One endpoint, three modes: structured (exact filters), semantic (meaning), and hybrid (both, fused — the best default for free text):

# Hybrid search: keywords + meaning (best default for free text)
curl -X POST https://api.gemina.co/api/v1/retrieval/query \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "hybrid",
    "text": "the credit note about the server outage",
    "topK": 10,
    "filters": { "issueDateFrom": "2026-01-01" }
  }'

Exact aggregations

Sums, averages and counts are computed in the database — never estimated by an AI model. Amounts in different currencies are never mixed into one total:

# Exact totals per vendor, computed in the database (never estimated by AI)
curl -X POST https://api.gemina.co/api/v1/retrieval/aggregate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": [{ "op": "sum", "field": "total_amount" }, { "op": "count" }],
    "groupBy": ["vendor_name", "currency"],
    "filters": { "issueDateFrom": "2026-01-01", "issueDateTo": "2026-03-31" }
  }'

Chat with your documents

Ask in natural language; Gemina routes the question to the right engine and answers grounded in your documents, with citations:

# Grounded natural-language Q&A over your document collection
curl -X POST https://api.gemina.co/api/v1/chat/query \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "message": "How much did we spend at Acme Catering this quarter?" }'
{
  "answer": "You spent a total of 18,450 ILS at Acme Catering this quarter, across 12 invoices.",
  "citations": ["5f2b7c1e-2b6b-4f0e-9a1c-4d3f2e1a0b9c"],
  "intent": "aggregation",
  "confident": true
}

For end-user-facing apps, exchange your API key server-side for a short-lived, scoped session token (POST /api/v1/sessions/token) — the browser can then query and chat safely without ever seeing your API key. The same capabilities are available to AI agents as MCP tools (query_documents, aggregate_documents, index_document).

Ship a chat experience without building one. @gemina/elements is a drop-in React chat component (<GeminaChat>) with a security-hardened token manager — citations, low-confidence handling, and RTL support included, without ever exposing your API key to the browser.

Install

npm i @gemina/elements @gemina/sdk react

Mint session tokens (Next.js App Router)

The API key stays on your server. This route mints a short-lived, scoped token for the browser — drop it in as-is:

// Next.js (App Router) — app/api/gemina-session/route.ts
import { NextResponse } from "next/server";
import { GeminaClient } from "@gemina/sdk";

const gemina = new GeminaClient(process.env.GEMINA_API_KEY!);

export async function POST(request: Request) {
  const user = await requireYourAppAuth(request); // your session check
  const minted = await gemina.sessions.mintRetrievalToken({
    sessionTokenInDTO: { endUserId: user.id, ttlSeconds: 900 },
  });
  return NextResponse.json({ token: minted.token, expiresIn: minted.expiresIn });
}

Render the chat

Point the token manager at that route and render the component — conversation memory and the session token are managed for you:

import { GeminaTokenManager } from "@gemina/elements/token-manager";

const tokenManager = new GeminaTokenManager({
  // Points at YOUR backend — see the mint endpoint below.
  fetchToken: async () => {
    const res = await fetch("/api/gemina-session", { method: "POST" });
    if (!res.ok) throw new Error("Failed to mint Gemina session token");
    return res.json(); // { token, expiresIn }
  },
  // Optional: seconds before expiry to refresh (default 60).
  refreshSkewSeconds: 60,
});

import { GeminaChat } from "@gemina/elements";

<GeminaChat
  tokenManager={tokenManager}
  onCitationClick={(documentId) => openDocumentViewer(documentId)}
/>;

Response Fields

The response includes structured extraction values keyed by extraction type. Unpopulated fields are null.

invoice_headers fields

Each header field uses an envelope shape: { value, coordinates, confidence }. When the invoice doesn't print the value, the whole envelope is null — a defensive client can safely guard with if (response.discountAmount) { … }.

  • grossSubtotalAmount — Sum of line items before any header-level discount or rounding.
  • discountAmount — Header-level discount in the document's currency. Sign is verbatim from the invoice: some templates print positives (229.91), others negatives (-229.91) or parenthesized values. Clients that subtract on their side must handle both.
  • discountPercentage — Header-level discount as a percentage (e.g. 3.0 means 3%). Only populated when the invoice prints it.
  • roundingAmount — Rounding adjustment (e.g. "round off", agorot rounding). Signed as printed; magnitude typically < 1.0 in document currency.
  • subtotalAmount — The tax base: the value the invoice's VAT/tax percentage is calculated against, after any header-level discount and rounding, before tax.

The reconciliation identity (modulo printing artifacts):

subtotalAmount + Σ taxes[].amount ≈ totalAmount

invoice_line_items fields

Each item in the line_items array is a flat object (no envelope). Unpopulated fields are null.

  • listPrice — Gross/catalog unit price before any line-level discount. Populated only when the invoice prints a dedicated "list price" / "catalog price" / "MSRP" column. Documentation-only — do not use it in line-total math.
  • unitPrice — NET price per unit, after any line-level discount. The lineTotal math uses this value, so the per-line discountAmount and discountPercentage should not be subtracted again. For the gross/catalog price, use listPrice when populated.
  • packagingAmount — Additive packaging charge (crate fee, palletizing fee). Positive. Contributes to lineTotal.
  • depositAmount — Additive deposit/refund charge (bottle deposit, container deposit). Positive. Contributes to lineTotal.
  • unitsPerPackage — Structural pack size: whole-number count of units per package (e.g. 24 cans per case). Informational; never a volume or weight.
  • packageQuantity — Order quantity in package units; may be fractional (e.g. 2.1 cartons). Informational. Most invoices print only one of unitsPerPackage or packageQuantity — both can be null independently.

Line-total math contract:

lineTotal ≈ quantity × unitPrice
          + taxAmount        (if present)
          + packagingAmount  (if present)
          + depositAmount    (if present)

When both pack-size fields are present, quantity ≈ packageQuantity × unitsPerPackage — the relationship is approximate, not enforced.

API Reference

Official SDKs

  • @gemina/sdk - TypeScript / Node.js (npm)
  • gemina - Python (PyPI)
  • Gemina.Sdk - C# (NuGet)
  • co.gemina:gemina-sdk - Java (Maven)
  • gemina/sdk - PHP (Packagist)
  • @gemina/elements - React chat UI (npm)

Extraction Types

  • invoice_headers - Invoice header fields (field list →)
  • invoice_line_items - Line item details (field list →)
  • ocr - Full text extraction
  • document_details_hebrew - Hebrew documents

Model Types

  • velox - Fast processing
  • praetorian - Balanced accuracy
  • invictus - Highest accuracy

Endpoints

  • POST /api/v1/documents/uploads
  • POST /api/v1/documents/uploads/web
  • GET /api/v1/documents/{id}
  • GET /api/v1/documents/results/{id}

Response Statuses

  • success - Extraction completed
  • pending - Job queued
  • in_process - Processing
  • failed - Error occurred

Document Intelligence

  • POST /api/v1/retrieval/query
  • POST /api/v1/retrieval/aggregate
  • POST /api/v1/chat/query
  • POST /api/v1/sessions/token

FileTag API

Ready to Get Started?

Sign up for a free trial and start extracting data from your documents today.