---
title: "Grant a principal access to an evidence asset"
method: POST
path: "/v1/evidence/{assetId}/grants"
tags: ["Evidence"]
---

# Grant a principal access to an evidence asset

`POST /v1/evidence/{assetId}/grants`

## Request

- Base URL: `https://brain.inite.ai`
- URL: `https://brain.inite.ai/v1/evidence/{assetId}/grants`
- Auth: HTTP bearer

Adds one ownership row (migration 0122) for a `user` handle or an installed `pack`. Idempotent over the live (asset, ownerKind, ownerId) triple: a repeat returns the standing grant with `created: false`. `ownerKind: "system"` is REFUSED (400) — a system grant never dies with a user, so a client could pin content past GDPR erasure with it; system ownership stays a write-seam property of registration. The grantee handle is never checked for existence (a grant to an unknown handle is inert), so this is no user-enumeration oracle. There is no grant expiry: 0122 has no such column, an `expiresAt` field is rejected rather than silently ignored, and the asset’s own `retainUntil` — echoed here — is the horizon, since retention purges asset and grants together. Acting requires the caller to pass the raw-read gateway’s own fences over the asset: tenant (the lookup runs inside the authenticated tenant), liveness (not tombstoned, quarantine clean-or-absent, retention horizon not already past), OWNERSHIP (at least one live grant exists, and a user-bound key must hold the end user’s own live user grant), and the media-PII polarity (unclassified blocked, `[]` open, classified needs `brain:read_media`) — nobody shares what they cannot read. The byte-delivery steps are deliberately NOT applied (no bytes move here), so a metadata-only `external` asset is shareable; every serve still re-runs the full gateway ladder against the grantee. Any denial is the same bare 404 — unknown, foreign and forbidden are indistinguishable. 404 until `EVIDENCE_GRANTS_API_ENABLED=1` AND `EVIDENCE_SUBSTRATE_ENABLED=1` (raised in a guard, before body validation could reveal the route). Source: src/evidence/evidence-grants.controller.ts.

Required scope: `brain:write`.

## Path parameters

- `assetId` string, required

## Request body

- GrantEvidenceAccessRequest
  - `ownerId` string, required
  - `ownerKind` 'user' | 'pack', required
  - `purpose` string

## Response `201`

The live grant (created now, or the standing one).

- GrantEvidenceAccessResponse
  - `assetId` string, required
  - `created` boolean, required
  - `grantId` string, required
  - `retainUntil` string, nullable, required

## Other responses

- `400`: Validation failed.
- `401`: Missing or unknown bearer key.
- `403`: The key lacks the required scope.
- `404`: No such resource in this tenant.

---

[API](https://skmtc.dev/inite/apis/inite-brain-platform-api.md) · [All operations](https://skmtc.dev/inite/apis/inite-brain-platform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/inite/apis/inite-brain-platform-api/revisions/c7c3eb5e304b?raw)
