COSTRINITY Ingest API

The ingest API from COSTRINITY — 1 operation(s) for ingest.

GitHub OpenAPI

Operations 1

POST /api/ingest Ingest agent event (single or batch) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/costrinity-xyz:costrinity-xyz-ingest-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

costrinity-xyz-ingest-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: VITNA API (Phase 1) Ingest API
  version: 1.1.0
  description: 'The compliance evidence layer for AI agents. Pre-flight compliance checks agents call before they act (consent, breach reportability, EU AI Act classification, cross-border), and audit-grade, signed records of what they did. Phase 1 surface covers region routing labels (one database region is in use today), DPIA/ROPA generation, Data Subject Rights (scoped: answerable only for events linked to a subject via principal_ref at ingest, from the point linkage begins; earlier events are permanently unattributable and cannot be linked retroactively), consent lifecycle, Sahamati AA integration, breach notification, and INR/Razorpay billing for DPDP-IN owners (switched off pending merchant onboarding). Also includes one-command agent self-provisioning (/api/setup, mints a restricted trial key with no signup), a destructive-action safety pre-flight (/api/preflight/action-check), scoped engagement sessions with signed evidence bundles (/api/engagement/*), and account claim by a person, from the claim link (/claim).'
  contact:
    name: COSTRINITY
    email: dpo@costrinity.xyz
    url: https://www.costrinity.xyz/
  license:
    name: Proprietary
    url: https://vitna.costrinity.xyz/terms
servers:
- url: https://vitna.costrinity.xyz
  description: Production
- url: https://vigil-staging.vercel.app
  description: Staging (preview branch)
tags:
- name: Ingest
paths:
  /api/ingest:
    post:
      summary: Ingest agent event (single or batch)
      description: 'An event''s payload is taken from payload, or from data, details, metadata, context or extra, and may be any JSON value. A JSON object is stored as sent, with the tracing and tool fields the event names added to it. A payload that is not a JSON object (text, a number, true or false, or an array) is kept whole as {"value": } and checked like any other field. Before the event is stored, each payload field in which the detectors find a leaked secret or a regulated identifier is replaced with "[redacted: ]", whatever its key is called (a key may contain dots, brackets or quotes, or be empty). Each piece of text, list or object in the payload that lies more than 32 levels deep, after the first 1,000 items of a list, or after the first 10,000 pieces of text in the payload is not read by the detectors and is replaced as a whole with "[redacted: beyond scan limit]"; a number, true or false, or null there is kept, unless its key is past the text limit. At most 1,000 such parts in one request, across all its payloads, are replaced where they are; a payload whose parts would go past that is replaced whole instead, with {"value": "[redacted: beyond scan limit]"}. The keys of the objects in the payload are read too, each as a piece of text before its value, and count toward those 10,000. Where a key holds a leaked secret or a regulated identifier, the object it is in is replaced as a whole with "[redacted: ]", or, for a key at the top level of the payload, the whole payload with {"value": "[redacted: ]"}. A key after the first 10,000 pieces of text is not read, so it and the keys after it in its object, with their values, are replaced together by one entry, "[redacted: beyond scan limit]", whose value is how many entries it replaces, and count as one such part; when the object already has a key of that name before them, that key''s value is replaced by the count. Before 2026-10-06 the detectors read 8 levels deep and the first 100 items of a list, read no keys, and left what lay beyond as sent. The tracing and tool fields added to the payload are part of it, so they are replaced with it. A payload with any such part counts as one match, labelled "beyond scan limit", in the summary added to the message, however many parts were replaced, and raises no severity; events stored before 2026-10-06 are unchanged. Before 2026-10-05 a payload sent as text was stored one character per key and was not checked, a number or true or false was dropped, and a secret under a key containing a dot or a bracket was stored as sent. Events stored before that are unchanged. An event''s type is read from event_type or, when that is absent or null, from the first of action, type, kind, event and name that is present, and it must be text: an event whose type is a list, an object, a number, or true or false is refused with 400 event_type_not_text, and nothing in the request is stored, the rest of a batch included. Before 2026-10-06 such a type was not checked: one event failed with 500, and in a batch it could be stored as sent. An event cannot claim to be a command from the owner: an event whose type is command (from event_type, action, type, kind, event or name, in any letter case), or that carries issued_by dashboard at its top level or at its payload''s, is refused with 400 owner_command_refused, and nothing in the request is stored, the rest of a batch included. Only POST /api/commands, for the signed-in owner, stores a command. Before 2026-10-06 an agent could store an event of type command with issued_by dashboard here, and its own live stream sent it back as a command frame.'
      tags:
      - Ingest
      security:
      - ApiKey: []
      - Bearer: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              description: 'One event (the properties below), an array of events (up to 500), or {events: [...]}. Each event may carry more fields than are listed here (status, tool_name, run_id and others).'
              properties:
                event_type:
                  type: string
                  description: Text. When it is absent or null, the first of action, type, kind, event and name that is present is read instead. A type that is not text is refused with 400 event_type_not_text.
                message:
                  type:
                  - string
                  - 'null'
                payload:
                  description: 'Optional. Any JSON value. A payload that is not a JSON object (text, a number, true or false, or an array) is kept whole as {"value": <the payload as sent>} and checked like any other field.'
                storage_mode:
                  type: string
                  enum:
                  - cloud
                  - local
      responses:
        '200':
          description: 'OK. Single event: { success, storage_mode, event_id, timestamp }; batch (up to 500): { success, storage_mode, count, events }. With storage_mode=local the events are checked and returned, not stored.'
        '202':
          description: Every event in the batch was a heartbeat and the agent blocks heartbeats; nothing stored
        '400':
          description: 'wrapper_field_invalid: a wrapped batch ({agent_name, events: [...]} and the like) has an agent_name, source, service, run_id or session_id that is neither a number nor text of at most 256 characters. Ingest copies those into every event, so it is refused as {error, message, refused: [<field>, ...]} and nothing is stored (2026-10-06). event_type_not_text: an event''s type is not text. owner_command_refused: an event claims to be a command from the owner. Either is {error, message, refused: [{index, field}]}, where index is the event''s place among the events read from the body; field is where the type was read from (event_type, action, type, kind, event or name) for event_type_not_text, and event_type, issued_by or payload.issued_by for owner_command_refused. A request with an event whose type is not text is answered event_type_not_text. Nothing in the request is stored.'
        '401':
          description: Invalid API key
        '402':
          description: The batch would exceed the agent's SOL budget; a budget_exceeded event is recorded instead
        '413':
          description: Body or batch too large
        '429':
          description: Rate limited
      operationId: postApiIngest
      x-operation-id-source: derived
components:
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: x-vigil-key
      description: 'Per-agent API key. Format: vitna_<64-char-hex>; legacy vigil_ keys remain valid. Send on /api/ingest.'
    Bearer:
      type: http
      scheme: bearer
      bearerFormat: vitna_<key> (legacy vigil_<key> accepted)
      description: 'Same key as x-vigil-key, sent as `Authorization: Bearer <key>`.'
    Session:
      type: apiKey
      in: cookie
      name: vigil_session
      description: 'Operator dashboard session cookie. Set by a sign-in: the second step of a password sign-in (/api/auth/verify-pin), or, for an address whose mail Google handles (Gmail or a Google Workspace domain) while VITNA''s mail cannot reach Gmail, the password alone (/api/owners); GitHub; a passkey; or a claim.'
    CronSecret:
      type: http
      scheme: bearer
      description: Bearer ${CRON_SECRET}. Only the Vercel cron infrastructure should hold this.