Docs

API log

Every call your keys made, with the request and the response redacted, kept seven days.

View as Markdown

When an agent calls the API and something goes wrong, the answer alone rarely says why. The API log keeps one row per authenticated call: the request id, the method, the path, the status, the duration, the key id and the SDK recognised from the User-Agent. Reading it needs audit_read, which a member never holds, so the log is an owner and admin surface.

Read it

cURL
curl -s https://api.mueslimail.com/v0/logs?limit=20 \
  -H "Authorization: Bearer ***"

Filters: key_id, method, status, sdk, after and before (RFC 3339, before exclusive, which is how you page). GET /v0/logs/{log} returns the same row with ip, user_agent and the two redacted bodies. A row of another organization is 404, never 403.

What is redacted, and how you can tell

  • A credential field (authorization, secret, api_key, token, password, cookie) becomes {"redacted": "credential"}.
  • A mail field (subject, text, html, raw, attachment content) becomes {"redacted": "mail_content", "bytes": N, "content_type": "...", "sha256": "..."}.
  • A header outside the allowlist (accept, content-type, idempotency-key, user-agent, x-mai-api-version, x-request-id) is not kept at all, and its name is listed when it was a credential header.
  • Anything that looks like a token, a whsec_ signing secret or a bearer value is masked even inside a free text field.
  • redactions names every removal, as header:authorization, mail_content:html or credential:secret: the list is the proof, not the promise.
  • A response body that is not JSON or markdown is not stored at all; bytes and sha256 still describe it.

SDK and version

The User-Agent of our own SDKs carries the name and the version (aimulti-node/1.2.0, aimulti-go/v1). The log normalises it into sdk and sdk_version, compares it with the newest version this deployment knows and sets sdk_update_available with a sentence a person can act on. Nothing is fetched from outside: MAI_SDK_LATEST (node=1.3.0,go=1.1.0) is what the comparison uses, so shipping a new client does not need a new API release. The same notice arrives as the X-Mai-Sdk-Update header on the call itself, so an integrator sees it without opening the console.

Copy for an assistant

GET /v0/logs/{log}/export answers text/markdown: the shape of the call, the redacted bodies and an explicit list of what the log does not contain. It is a copy a person pastes where they work, not an integration: nothing is sent to a third party and no external service is called.

Retention

Seven days, enforced by the retention job: the row is deleted by age and not by request. The table is in the same purge job and the same weekly restore drill as every other table, so the published window is a fact and not a target.

GET/v0/logs

Redacted API request log of the organization, newest first (requires audit_read)

Parameters

NameTypeNotes
limitintegerin query, min 1, max 200, default 50
key_idstring (uuid)in query
methodstringin query
statusintegerin query, min 100, max 599
sdkgo | node | python | php | ruby | javain query
afterstring (date-time)in query
beforestring (date-time)in query

Responses

  • 200 OK: object
  • 400 Error envelope: Error
  • 403 Error envelope: Error
  • 501 Error envelope: Error

GET/v0/logs/{log}

One API request with its redacted request and response body (requires audit_read)

Parameters

NameTypeNotes
logstring (uuid)in path, required

Responses

  • 200 OK: RequestLog
  • 403 Error envelope: Error
  • 404 Error envelope: Error
  • 501 Error envelope: Error

GET/v0/logs/{log}/export

The same request as a markdown block a person can paste into an assistant (requires audit_read)

Parameters

NameTypeNotes
logstring (uuid)in path, required

Responses

  • 200 OK: string
  • 403 Error envelope: Error
  • 404 Error envelope: Error
  • 501 Error envelope: Error

RequestLogEntry object

NameTypeNotes
idstring (uuid)
request_idstringThe X-Request-Id of the call
key_idstring (uuid)The key that made the call; absent for a person signed in
actorstringuser:<id> or api_key:<id>
actor_typeuser | agent
methodstring
pathstringPath and filters, with the value of a credential-shaped query parameter masked
statusinteger
duration_msinteger
sdkstringSDK name normalised from the User-Agent; absent when the caller is not one of our SDKs
sdk_versionstring
sdk_update_availablebooleanA newer version of that SDK exists on this deployment
sdk_lateststring
sdk_noticestringThe sentence to show a person: which SDK, which version, which one to move to
created_atstring (date-time)

RequestLogBody object

NameTypeNotes
headersobjectRequest headers only, from an allowlist
bodyobjectThe JSON as it was, with credentials and mail content replaced by markers
bytesintegerSize of the original body
sha256stringDigest of the original body, empty when there was none
truncatedbooleanThe stored body is a summary: the original was larger than the log keeps