M365 Copilot document-AI actions (/copilot/v1)
The /copilot/v1 surface lets a Microsoft 365 Copilot integration run document-AI actions
(rewrite, summarize, translate, redact) on a user's document text through a Myra gateway. It is a
separate front door from the inference API: identity comes from a Microsoft
Entra access token (not a Myra gateway token), and the model, provider, tools, and system prompt
are server-pinned — the caller supplies only an action and the document text.
This surface is a paid, per-tenant feature and ships dark. It answers only when a platform admin has both provisioned the Entra app (see Settings) and enabled document AI for the tenant (see enabling below); until then every request is rejected.
Authentication
Send a Microsoft Entra OAuth2 access token (a delegated, user-present token minted for the Myra Copilot app) as a bearer:
The token is validated at the trust boundary (signature against the tenant's Entra JWKS, issuer pin,
aud/azp against the platform-configured Myra app, mandatory exp, a delegated-token scp gate —
and, when copilot_entra_scope_required is configured,
that the scp carries that specific scope).
The Myra tenant is resolved from the token's directory id (tid) and the user from its object
id (oid) — both must already be provisioned in Myra (via SSO/SCIM). Nothing in the request body or
headers can set the tenant, user, gateway, or model.
Endpoints
POST /copilot/v1/rewrite
POST /copilot/v1/summarize
POST /copilot/v1/translate
POST /copilot/v1/redact
Request body
A single JSON object (hostile input — validated at the boundary):
| Field | Type | Rules |
|---|---|---|
text |
string, required | The document text or selection to act on. Non-empty (blank/whitespace-only is rejected). At most 256 KiB; larger is rejected 413 (never truncated). |
params |
object, optional | Per-action parameters (below). Must be a JSON object if present. Each value is trimmed, control characters collapsed to spaces, and capped at 2000 bytes. |
Per-action params:
- rewrite —
instruction(optional): style/tone guidance folded into the system prompt. - summarize —
length(optional): a target-length hint. - translate —
target_language(required): the language to translate into. - redact — none.
The gateway builds a server-pinned request from a fixed per-action template: the tenant's designated
document-AI model, a gateway-authored system prompt (a sanitized param fills one bounded
slot), and the document text as the sole user message. Any model, tools, system, or tenant
field in the body is ignored.
Response 200
The response is the standard OpenAI chat-completions JSON; the result text is
choices[0].message.content.
redact runs entirely locally (the gateway's PII detectors mask the text — no model/upstream
call) and returns the masked text in the same chat-completions envelope ("model": "redact"). redact
never returns text that was not actually masked — it fails closed on every path where masking did not
run: the designated gateway would not mask the request (no masker, or only flag/disabled/response-only
detectors) → 500 configuration_error; a content-policy guardrail blocked the content →
400 guardrail_blocked; the PII scan degraded (a masker/analyzer outage passed the text through) →
503. A gateway that cannot redact never claims to have.
Errors
| Status | When |
|---|---|
401 unauthorized |
Missing / malformed / repeated Authorization header, or an invalid Entra token (bad signature, issuer, aud/azp, tid, missing scp/oid/exp, expired). A single generic 401 — the failed check is never disclosed. |
403 forbidden |
The directory (tid) maps to no Myra tenant; the user (oid) is not provisioned; the token lacks the required scope (when configured); document AI is not enabled for the tenant, or its designated gateway/model is missing or points at another tenant's gateway. |
405 method_not_allowed |
Anything other than POST. |
400 invalid_request |
Unknown action; empty/blank text; text/params of the wrong type; translate without params.target_language; a blank/oversized param. |
400 guardrail_blocked |
(redact only) a content-policy guardrail blocked the content — it is not returned. |
413 request_too_large |
text over the 256 KiB semantic cap (or the whole request payload over the ~512 KiB pre-decode bound). |
429 rate_limited / 429 quota_exceeded |
The designated gateway's rate limit / budget was hit (identical to /v1). |
503 service_unavailable |
A backing-store fault during authentication, or (redact only) the PII scan degraded (a masker/analyzer outage) so masking did not run — retryable. |
500 configuration_error |
(redact only) the designated gateway would not mask the request (no scrub-action masker), or the masking pipeline returned an unreadable body — the gateway refuses rather than return an unmasked/empty result. |
Enabling document AI for a tenant
Provisioning is split by role (the client is never the authorization boundary):
A partial update — only the fields present in the body are applied.
| Field | Who | Meaning |
|---|---|---|
document_ai_enabled |
platform admin | The paid entitlement flag (true/false). |
entra_tenant_id |
platform admin | The tenant's Entra directory GUID (the tid→tenant routing claim). Globally unique: a GUID already mapped to another tenant is rejected 409. null clears it. |
document_ai_gateway_id + document_ai_model |
tenant admin | The gateway the actions run on and the model on it. Set as a pair (both strings to designate, both null to clear). The gateway must belong to this tenant, be purpose=production, and serve the model — otherwise 400. |
A non-platform-admin that sends document_ai_enabled or entra_tenant_id gets 403. Every applied
change is recorded in the audit log. Requires the TENANT_SETTINGS_MANAGE permission;
a tenant admin may only touch their own tenant.