Skip to content

Retrieve a log

A single API call you made, with the exact body you sent and the exact body we answered with.

http
GET /v1/logs/{id}

Requires a Full access API key.

Path parameters

ParameterTypeRequiredDescription
idstringYesThe log id. This is the same value as the request_id on an error and the X-Request-Id header on every response.

Example

bash
curl "https://api.sendgrail.com/v1/logs/$LOG_ID" \
  -H "Authorization: Bearer $SENDGRAIL_API_KEY"
js
const res = await fetch(`https://api.sendgrail.com/v1/logs/${id}`, {
  headers: { Authorization: `Bearer ${process.env.SENDGRAIL_API_KEY}` },
});

const log = await res.json();

Response

json
{
  "object": "log",
  "id": "a816c847-153d-4be8-a43e-f05391ff01e6",
  "created_at": "2026-07-14T20:20:00+00:00",
  "endpoint": "/v1/contacts",
  "method": "POST",
  "response_status": 422,
  "latency_ms": 13,
  "ip": "127.0.0.1",
  "user_agent": "curl/8.7.1",
  "api_key": {
    "id": "c7e0321e-95f2-478f-8af1-bbe1f705e5ce",
    "name": "dev-test"
  },
  "request_body": {
    "email": "not-an-email"
  },
  "response_body": {
    "message": "The email field must be a valid email address.",
    "code": "validation_error",
    "errors": { "email": ["The email field must be a valid email address."] },
    "request_id": "a816c847-153d-4be8-a43e-f05391ff01e6"
  }
}

request_body and response_body are whatever that call carried, so their shape depends on the endpoint that was called. They appear only here — the list leaves them out, because a page of a hundred bodies would be megabytes you didn't ask for.

An error tells you where its own log is

Every failure carries a request_id, and that is the log id:

json
{
  "message": "The email field must be a valid email address.",
  "code": "validation_error",
  "request_id": "a816c847-153d-4be8-a43e-f05391ff01e6"
}

So the debugging loop is closed without you having to search for anything: take the request_id from the error, GET /v1/logs/{that id}, and you are looking at exactly what you sent and exactly what we said. The same value is on the X-Request-Id header of every response, including successful ones — log it on your side and any call you ever made is one request away.

Fields

FieldTypeDescription
objectstringAlways "log".
idstringThe log id — and the request_id of that call.
created_atstringISO 8601 timestamp of when the call arrived.
endpointstringThe path called, e.g. /v1/emails.
methodstringGET, POST, PATCH or DELETE.
response_statusnumberThe HTTP status we answered with.
latency_msnumberHow long we took, in milliseconds.
ipstringThe IP the call came from.
user_agentstring | nullThe caller's User-Agent, if it sent one. It is not required.
api_keyobject | nullThe key used: its id and name. Never the key itself.
request_bodyobject | string | nullThe body you sent. Retrieve only.
response_bodyobject | string | nullThe body we answered with. Retrieve only.

Very large bodies are truncated when recorded. A truncated body can no longer be parsed as JSON, so it comes back as a string rather than an object — you see what was actually stored instead of nothing at all.

What is not in a log

Never the API key. A log names the key by id, so you can tell which key made a call, but the token itself is not recorded and cannot be recovered from here.

Attachment bytes. A base64 attachment on a send is replaced with [base64 omitted] before the body is stored — otherwise every log of a send would carry a copy of the file.

Logs are read-only

There is no endpoint to edit or delete a log. A log is evidence: if a key could erase its own trail, the trail would be worth nothing precisely when it mattered. Logs age out on a retention schedule instead — see List logs.

Transactional email on your own domain.