Developers

Build on your company's brain.

Find evidence, ask questions with sources, and send notes back for processing and review.

First call

  1. Open your company menu in Abrain, then API keys. An organization admin can create a named key with Read knowledge permission.
  2. Install the SDK and store the key in your server's environment.
  3. Run a search against knowledge your company has already captured or indexed.
npm install https://docs.abrain.no/downloads/abrain-sdk-0.1.0.tgz
ABRAIN_API_KEY=your_key_here
import { Abrain } from "@abrain/sdk";

const brain = new Abrain({ apiKey: process.env.ABRAIN_API_KEY });
const found = await brain.search({ q: "Campaign launch" });
const answer = await brain.think({ q: "What launch date was agreed?" });

console.log(found.results, found.armsFailed);
console.log(answer.answer, answer.citations, answer.gaps);

The key stays on your server. The browser calls your application; your application calls Abrain. An empty brain can return no evidence. Search reports partial retrieval through armsFailed; an answer can be null while evidence and gaps remain available.

Download the runnable customer briefing · OpenAPI contract · Endpoint reference

Access and permissions

Use Authorization: Bearer <key>. A key belongs to one company. The API reads ungrouped public and internal records; restricted, confidential and group-restricted records are excluded. The caller cannot select a different tenant.

  • brain:read: search, cited answers, entity health.
  • captures:write: submit notes under this credential's source.
  • captures:read: read this credential's capture receipts and status.

Capture permission grants no review, claim-confirmation, finance or administration access. Revocation is checked on every API-key request. A replacement key has a new capture identity; old captures remain recorded, but their status is not readable with the replacement key.

Existing Abrain OAuth and machine tokens also work through accessToken. Their current scopes still apply; existing brain:write grants permit capture and status. The public API applies the same public/internal reading limit. These tokens do not become new grants.

const brain = new Abrain({
  accessToken: async () => getExistingAbrainAccessToken(),
  baseUrl: "https://staging.abrain.no",
});

Send a note and follow its status

const receipt = await brain.captures.create({
  body: "Meeting note: the launch date remains unconfirmed.",
  sourceId: "meeting-2026-09-05-42",
});
const status = await brain.captures.status(receipt.id);

A receipt means the note is stored. Extraction and wiki review happen asynchronously. Use a stable sourceId for each source record. Retrying identical content, classification and occurrence time returns the same capture with created: false. Different content under that ID returns 409 source_id_conflict. The API assigns the source from the credential.

processingStatus: new, extracted or failed. propagationStatus: null until recorded, then pending, drafted, merged, skipped, rejected or failed. A merged wiki edit does not confirm every extracted claim. Processing may be unavailable; poll with a delay and a deadline and keep pending or unknown states visible.

Connect an agent with MCP

Abrain's existing remote MCP server is available at https://abrain.no/api/mcp. In a client that supports remote HTTP MCP and OAuth, add this URL and complete the browser sign-in for your company. Use the client's remote-server setup, including its current OAuth flow.

MCP uses OAuth permissions. Organization API keys in this guide apply to /api/v1; they are not MCP credentials. The SDK and MCP use the same underlying brain functions.

Errors and limits

Errors are JSON with error, message and requestId. The SDK throws AbrainError with HTTP status, code, request ID and retry delay. Missing or revoked credentials return 401; insufficient permission returns 403; unavailable records return 404; conflicts return 409; oversized bodies return 413; rate limits return 429 with Retry-After.

The SDK has a 65-second default timeout and makes no automatic retries. Retry reads with backoff after 429 or 503. Retry captures only with the same source ID and unchanged input. The maximum capture is 1 MB in UTF-8; source IDs are at most 512 characters.

Limits are per credential per minute: search, health and status 120; think 10; capture 30. Each company also has a shared limit ten times the per-credential limit. API responses are not cached.

Endpoint reference

Generated from the same contract as the SDK and runtime validation. Base URL: https://abrain.no.

Answer with citations and gaps

POST /api/v1/think

Requires brain:read. Limit: 10 requests per minute per credential; tenant limit is ten times this.

Request schema
{
  "type": "object",
  "properties": {
    "q": {
      "type": "string",
      "minLength": 1,
      "maxLength": 500
    },
    "anchor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 500
    }
  },
  "required": [
    "q"
  ],
  "additionalProperties": false
}
Response schema
{
  "type": "object",
  "properties": {
    "question": {
      "type": "string"
    },
    "anchor": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "person",
                "organization"
              ]
            },
            "slug": {
              "type": "string"
            },
            "name": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "slug",
            "name"
          ]
        },
        {
          "type": "null"
        }
      ]
    },
    "answer": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "citations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "n": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "tier": {
            "anyOf": [
              {
                "type": "number",
                "const": 0
              },
              {
                "type": "number",
                "const": 1
              },
              {
                "type": "number",
                "const": 2
              }
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "claim",
              "interaction",
              "document",
              "search"
            ]
          },
          "id": {
            "type": "string"
          },
          "slug": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "title": {
            "type": "string"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "n",
          "tier",
          "kind",
          "id",
          "slug",
          "title"
        ]
      }
    },
    "gaps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "no_evidence",
              "thin_evidence",
              "tier0_only",
              "no_interactions",
              "unresolved_participants",
              "contradicted",
              "uncited_claim",
              "unconfirmed",
              "stale",
              "no_synthesis"
            ]
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "detail"
        ]
      }
    },
    "evidence": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "n": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "tier": {
            "anyOf": [
              {
                "type": "number",
                "const": 0
              },
              {
                "type": "number",
                "const": 1
              },
              {
                "type": "number",
                "const": 2
              }
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "claim",
              "interaction",
              "document",
              "search"
            ]
          },
          "id": {
            "type": "string"
          },
          "slug": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "title": {
            "type": "string"
          },
          "detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "occurred_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              {
                "type": "null"
              }
            ]
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "n",
          "tier",
          "kind",
          "id",
          "slug",
          "title",
          "detail",
          "occurred_at"
        ]
      }
    },
    "model": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "latencyMs": {
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "question",
    "anchor",
    "answer",
    "citations",
    "gaps",
    "evidence",
    "model",
    "latencyMs"
  ]
}

Read the recorded gaps of one entity

GET /api/v1/health

Requires brain:read. Limit: 120 requests per minute per credential; tenant limit is ten times this.

Request schema
[
  {
    "name": "entity",
    "in": "query",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 500
    }
  }
]
Response schema
{
  "type": "object",
  "properties": {
    "count": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "entities": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "subjectType": {
            "type": "string",
            "enum": [
              "person",
              "organization",
              "document"
            ]
          },
          "subjectId": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "codes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "no_interactions",
                "dangling_link",
                "unresolved_participants",
                "contradicted",
                "uncited_claim",
                "unconfirmed",
                "stale"
              ]
            }
          },
          "staleDays": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "lastInteractionAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              {
                "type": "null"
              }
            ]
          },
          "proposedClaims": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "contradictedClaims": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "uncitedClaims": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "unresolvedMentions": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "danglingLinks": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "subjectType",
          "subjectId",
          "slug",
          "name",
          "codes",
          "staleDays",
          "lastInteractionAt",
          "proposedClaims",
          "contradictedClaims",
          "uncitedClaims",
          "unresolvedMentions",
          "danglingLinks"
        ]
      }
    }
  },
  "required": [
    "count",
    "entities"
  ]
}

Save a note for processing and review

POST /api/v1/captures

Requires captures:write. Limit: 30 requests per minute per credential; tenant limit is ten times this.

Request schema
{
  "type": "object",
  "properties": {
    "body": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1000000,
      "description": "Verbatim text; maximum 1 MB in UTF-8."
    },
    "sourceId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "Stable ID in this integration. Reusing it with different content returns 409."
    },
    "occurredAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "classification": {
      "type": "string",
      "enum": [
        "public",
        "internal"
      ]
    }
  },
  "required": [
    "body",
    "sourceId"
  ],
  "additionalProperties": false
}
Response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "source": {
      "type": "string"
    },
    "sourceId": {
      "type": "string"
    },
    "capturedAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "occurredAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "capturedBy": {
      "type": "string"
    },
    "classification": {
      "type": "string",
      "enum": [
        "public",
        "internal"
      ]
    },
    "processingStatus": {
      "type": "string",
      "enum": [
        "new",
        "extracted",
        "failed"
      ]
    },
    "propagationStatus": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "pending",
            "drafted",
            "skipped",
            "failed",
            "merged",
            "rejected"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "Null means no propagation outcome is recorded. Merged describes wiki edits, not confirmation of every claim."
    },
    "propagationUpdatedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "created": {
      "type": "boolean"
    }
  },
  "required": [
    "id",
    "source",
    "sourceId",
    "capturedAt",
    "occurredAt",
    "capturedBy",
    "classification",
    "processingStatus",
    "propagationStatus",
    "propagationUpdatedAt",
    "created"
  ]
}

Read this integration's capture status

GET /api/v1/captures/{id}

Requires captures:read. Limit: 120 requests per minute per credential; tenant limit is ten times this.

Request schema
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    }
  }
]
Response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "source": {
      "type": "string"
    },
    "sourceId": {
      "type": "string"
    },
    "capturedAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "occurredAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "capturedBy": {
      "type": "string"
    },
    "classification": {
      "type": "string",
      "enum": [
        "public",
        "internal"
      ]
    },
    "processingStatus": {
      "type": "string",
      "enum": [
        "new",
        "extracted",
        "failed"
      ]
    },
    "propagationStatus": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "pending",
            "drafted",
            "skipped",
            "failed",
            "merged",
            "rejected"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "Null means no propagation outcome is recorded. Merged describes wiki edits, not confirmation of every claim."
    },
    "propagationUpdatedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "source",
    "sourceId",
    "capturedAt",
    "occurredAt",
    "capturedBy",
    "classification",
    "processingStatus",
    "propagationStatus",
    "propagationUpdatedAt"
  ]
}

Product documentation