Documentation

Build with Intellign.

Intellign solves resource-allocation problems — who goes where, what runs when. This page covers the web app, the public API, the Python SDK, and the MCP server, all backed by the same account and engine.

docs v1.0.0 · last updated July 14, 2026

Overview — what Intellign does

Intellign solves resource-allocation problems: who goes where, what runs when, how to use limited people, vehicles, budget, or time well.

You describe a problem — “assign 40 nurses to 6 clinics so skills match and nobody is overloaded” — attach your data (a spreadsheet is enough, and if you have no data Intellign can generate a realistic sample), and get back an optimized, explainable plan. Real optimization engines do the math (genetic algorithms, CP-SAT, OR-Tools); AI handles the understanding, data-gap filling, and plain-language explanation.

The four ways to use Intellign

SurfaceBest forWhere
Web appAnyone — chat your way from problem to planintellign.ai
Public APIDevelopers integrating optimization into their systemsapi.intellign.ai/api/public/v1
Python SDKPython developers — typed client, no HTTP plumbingpip install intellign
MCP serverAI agents (Claude etc.) calling Intellign as toolsmcp.intellign.ai/mcp

All four run on the same engine and the same account: an API key made in the dashboard works for the API, the SDK, and the MCP server.

Five concepts (the whole mental model)

  1. Entities — the resources being assigned (nurses, vans, field officers). A table with an id column.
  2. Targets — what receives them (clinics, routes, districts). Also a table.
  3. Objectives — what “good” means, with weights. E.g. skill_match at weight 70, workload_balance at 30.
  4. Constraints — hard rules the plan should respect, written in a small formal grammar: entity.skills superset_of target.required_skills.
  5. Jobs — solving runs asynchronously; you submit a problem, get a job_id, and poll / stream / get a webhook when the plan is ready.

Getting started

  1. Create an account at intellign.ai.
  2. Create an API key: Dashboard → API Keys.
    • ik_live_... — production. Solves count against your plan.
    • ik_test_...sandbox: free, doesn't touch quota, capped at 200 entities / 200 targets and shorter solver runs. Start here.
    • The key is shown once — store it like a password.
  3. Pick your surface and run the 2-minute example in its section below.

Plans & limits (per key, per minute): free 30 requests, pro 120, enterprise 600. Monthly solve quotas follow your org's plan.

Public API

Base URL: https://api.intellign.ai/api/public/v1
Auth on every request: Authorization: Bearer ik_...
Interactive OpenAPI docs: api.intellign.ai/docs

Two-minute example (curl)

KEY=ik_test_...

# 1. Create a problem (inline data)
curl -s -X POST https://api.intellign.ai/api/public/v1/problems \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{
  "spec": {
    "problem_type": "assignment",
    "entities": {"rows": [
      {"id": "n1", "skills": "triage"}, {"id": "n2", "skills": "surgery"}]},
    "targets": {"rows": [
      {"id": "c1", "required_skills": "triage", "capacity": 1},
      {"id": "c2", "required_skills": "surgery", "capacity": 1}]},
    "objectives": [{"metric": "skill_match", "weight": 70,
                    "entity_column": "skills", "target_column": "required_skills"}],
    "constraints": [{"expression": "entity.skills superset_of target.required_skills"}]
  }}'
# → {"problem_id": "...", "status": "draft", "goals_compiled": 2}

# 2. Solve it
curl -s -X POST https://api.intellign.ai/api/public/v1/problems/<problem_id>/solve \
  -H "Authorization: Bearer $KEY"
# → {"job_id": "..."}

# 3. Poll, then fetch the plan
curl -s https://api.intellign.ai/api/public/v1/jobs/<job_id> -H "Authorization: Bearer $KEY"
curl -s https://api.intellign.ai/api/public/v1/jobs/<job_id>/result -H "Authorization: Bearer $KEY"

The ProblemSpec format

Everything the headless API solves is a ProblemSpec (version "1"):

{
  "spec_version": "1",
  "problem_type": "assignment",       // assignment | scheduling | routing | allocation | matching
  "name": "optional label",
  "description": "optional — used when explaining results",
  "entities": {"rows": [...]},          // OR {"dataset_id": "ds_..."}; id column default "id"
  "targets":  {"dataset_id": "ds_..."},
  "objectives": [                        // at least one
    {"metric": "skill_match", "weight": 70, "direction": "maximize",
     "entity_column": "skills", "target_column": "required_skills"}
  ],
  "constraints": [                       // optional
    {"expression": "entity.skills superset_of target.required_skills", "hard": true}
  ],
  "solver_params": {"time_budget_seconds": 60, "generations": null,
                    "population_size": null, "solver_preference": null},
  "quality_mode": "balanced"            // fast | balanced | best
}

Rules: inline rows max 2,000 (use datasets beyond that); weights (0, 100]; exactly one of rows/dataset_id per side.

Objectives (metric catalog)

MetricMeaningNeeds
skill_matchentity's skill set covers the target's requirementsentity_column, target_column
distanceprefer geographically closer pairings (lat/lon columns)
workload_balancepenalize uneven load vs target capacitytarget_column
attribute_matchexact categorical matchentity_column, target_column
costminimize a numeric entity columnentity_column
preference_matchscheduling preference alignment

Always current version: GET /capabilities (also lists solvers, grammar, limits).

Constraint grammar

Six forms — anything else is rejected at create time with a precise reason:

FormExample
entity.{a} superset_of target.{b}entity.skills superset_of target.required_skills
entity.{a} <op> target.{b}entity.hours <= target.max_hours (<= >= == < >)
entity.{a} <op> <number>entity.age >= 21
sum(entity.{a}) <op> target.{b}sum(entity.load) <= target.capacity
haversine(entity.{a}, target.{b}) <= kmhaversine(entity.location, target.location) <= 25
entity.{a} in [values]entity.region in ['north', 'east']

"hard": true (default) = violation weight 100; soft = 60.

Endpoints

Method + pathPurpose
GET /capabilitiesMetric catalog, grammar, solvers, limits — machine-readable
GET /templates · GET /templates/{name}Starter ProblemSpecs to copy and edit
POST /datasets (multipart file)Upload CSV/XLSX (≤10 MB, ≤50k rows) → dataset_id; 7-day retention
GET /datasets/{id}Metadata + preview
POST /problemsRegister a spec → problem_id (validates + compiles immediately)
GET /problems/{id}Spec + status + last job
POST /problems/{id}/solveStart solving → job_id (202)
GET /jobs/{id}Status + progress (best_fitness, generation)
GET /jobs/{id}/streamServer-Sent Events live progress
GET /jobs/{id}/resultThe plan: assignments, metrics; 409 while running
POST /solveNatural-language one-shot: {problem, entities_dataset_id, targets_dataset_id}
POST /webhooks · GET /webhooks · DELETE /webhooks/{id}Completion notifications (max 5 endpoints)
GET /webhooks/{id}/deliveriesDelivery log (last 50 attempts)
POST /sessions · /sessions/{id}/messages · GET /sessions/{id}Conversational flow over the API
POST /sessions/{id}/optimize · GET /sessions/{id}/exportRun a ready session; export it as a ProblemSpec

Errors

Every error has one shape:

{"error": {"code": "problem_not_found", "message": "…", "request_id": "abc123"}}

Quote request_id when contacting support.

CodeHTTPMeaning
invalid_api_key401missing / unknown / expired key
insufficient_scope403key lacks solve or read scope
invalid_spec400/422bad spec, constraint, or upload — message lists every issue
*_not_found404dataset / problem / job / template / webhook / session
dataset_too_large413>10 MB or >50k rows
job_not_finished409result requested before completion
quota_exceeded402monthly plan limit reached
rate_limited429slow down — honor the Retry-After header
sandbox_limit_exceeded403ik_test_ size caps
idempotency_conflict409same Idempotency-Key currently in flight

Idempotency & retries

Send Idempotency-Key: <any-unique-string> on POSTs; replays within 24 h return the original response instead of duplicating work. Safe to retry 429/5xx with backoff.

Webhooks

POST /webhooks {"url": "...", "events": ["job.completed", "job.failed"]}
# → response includes a "secret" — shown once
  • Headers: X-Intellign-Event, X-Intellign-Signature: sha256=<HMAC-SHA256(secret, raw_body)>
  • Body: {"event": "job.completed", "data": {"job_id", "problem_id", "status", "best_fitness"}}
  • Verify with a constant-time compare on the raw body. Non-2xx → up to 3 attempts (1s/2s backoff), all logged in /webhooks/{id}/deliveries.

Python SDK

pip install intellign                  # core
pip install "intellign[pandas]"        # + result.to_dataframe()
pip install "intellign[ical]"          # + result.export_ical()
pip install "intellign[mcp]"           # + the MCP server

Python ≥ 3.10. Source + runnable examples: github.com/DataBacked-Africa/intellign-python

Two-minute example

from intellign import Client, Problem

client = Client(api_key="ik_test_...")   # base_url defaults to api.intellign.ai

problem = (
    Problem.assignment(name="Nurse assignment")
    .entities(rows=[{"id": "n1", "skills": "triage"},
                    {"id": "n2", "skills": "surgery"}])
    .targets(rows=[{"id": "c1", "required_skills": "triage", "capacity": 1},
                   {"id": "c2", "required_skills": "surgery", "capacity": 1}])
    .maximize("skill_match", weight=70,
              entity_column="skills", target_column="required_skills")
    .require("entity.skills superset_of target.required_skills")
    .quality("fast")
)

result = client.submit(problem).wait()
for a in result.assignments:
    print(a["resource_id"], "->", a["target_id"])

Client surface (sync Client / async AsyncClient — same methods)

AreaMethods
Discoverycapabilities(), templates(), template(name)
Dataupload_dataset(path), get_dataset(id)
Problemscreate_problem(spec_or_builder), get_problem(id), solve_problem(id), submit(spec_or_builder)
Natural languagesolve_nl(text, entities_dataset_id, targets_dataset_id)
JobsJob.status(), .wait(timeout=300), .result(), .stream_progress()
Results.assignments, .metrics, .best_fitness, .to_dataframe(), .export_ical(path)
Webhookscreate_webhook(url), list_webhooks(), delete_webhook(id), webhook_deliveries(id)
Sessionscreate_session(), send_message(id, text), session_state(id), optimize_session(id), export_session(id)

Builder verbs: .entities()/.targets() (rows or dataset_id=), .maximize()/.minimize(metric, weight, ...), .require(expr, hard=True), .quality("fast|balanced|best"), .time_budget(s), .generations(n), .population(n), .solver(name), .describe(text).build().

Async + live progress

from intellign import AsyncClient

async with AsyncClient(api_key="ik_...") as client:
    job = await client.submit(problem)
    async for event in job.stream_progress():
        print(event.get("current_generation"), event.get("best_fitness"))
    result = await job.result()

Error handling

Every API failure raises a typed subclass of IntelligError with .code, .status_code, .request_id: AuthenticationError, ScopeError, InvalidSpecError, NotFoundError, ConflictError, QuotaError, RateLimitError (.retry_after), SandboxLimitError, SolveFailedError, ServerError.

The client auto-retries 429/502/503/504 (honoring Retry-After), and create_problem/solve_problem accept idempotency_key=.

MCP server (Intellign for AI agents)

The MCP server exposes Intellign as tools any MCP-compatible AI can call. Hosted at https://mcp.intellign.ai/mcp (Streamable HTTP, multi-tenant: your request's Authorization header carries your key, so your quota, limits, and data scoping apply). A human-readable overview lives at mcp.intellign.ai.

Connect

Claude Code:

claude mcp add --transport http intellign https://mcp.intellign.ai/mcp \
  --header "Authorization: Bearer ik_..."

Claude Desktop (no remote header support yet — bridge via mcp-remote):

{"mcpServers": {"intellign": {
  "command": "npx",
  "args": ["-y", "mcp-remote", "https://mcp.intellign.ai/mcp",
           "--header", "Authorization: Bearer ${INTELLIGN_API_KEY}"],
  "env": {"INTELLIGN_API_KEY": "ik_..."}}}}

Local/offline alternative:

pip install "intellign[mcp]"
INTELLIGN_API_KEY=ik_... intellign-mcp

Tools

ToolWhat it does
list_templateslist starter problems
get_template(name)fetch a full editable ProblemSpec
create_problem(spec)register a spec → problem_id (returns readable errors the model can fix)
solve_problem(problem_id, wait=True, timeout_s=60)solve; returns assignments, or job_id if async
get_status(job_id) / get_result(job_id)poll long solves

Resources: intellign://schema/spec (ProblemSpec JSON schema), intellign://capabilities (live catalog).

Prompts that work well

  • “Get the nurse clinic assignment template from Intellign, replace the nurses with [my data], solve it, and show me who goes where.”
  • “Create an Intellign problem assigning these 12 drivers to 5 zones, maximizing zone preference match — read intellign://schema/spec first.”

Troubleshooting & FAQ

401 invalid_api_key
Key missing/typo'd, or revoked. Keys are shown once at creation; make a new one if lost.
403 sandbox_limit_exceeded
ik_test_ keys cap at 200 entities/targets. Use a live key for bigger problems.
422 invalid_spec
The message lists every problem (objectives[0]: entity_column is required…). Fix all listed items; the grammar section above shows valid constraint forms.
409 job_not_finished
The solve is still running; poll GET /jobs/{id} until status is completed/failed (compare case-insensitively), or use webhooks / job.wait().
429 rate_limited
Respect Retry-After. SDK does this automatically.
Webhook signature won't verify
You must HMAC the raw request body bytes, not re-serialized JSON.
Can Intellign generate data for me?
Yes, in the web app / sessions flow: describe the problem and ask for a sample dataset. The headless API expects you to bring data (inline rows or uploaded datasets).
How fresh are key/plan changes?
Auth context is cached up to 10 minutes; a revoked key or plan change can take that long to propagate.

Changelog

DateVersionAreaChange
2026-07-141.0.0allInitial version: API v1, SDK 0.1.x, MCP (hosted, multi-tenant)
Try the live demoLaunch appAsk a question