SendGrid Email Log
Retrieve activity for a specific SendGrid message with the Email Logs API.
Purpose
This skill is for reading SendGrid Email Logs and per-message activity.
It has two lookup paths:
scripts/get_email_log.sh calls GET /v3/logs/{sg_message_id} for one specific message and computes an event_summary. If the full sg_message_id is not known, it can first call POST /v3/logs with recipient and sent time to resolve the full ID, then automatically call the detail endpoint.
scripts/query_email_logs.sh calls POST /v3/logs to search Email Logs by recipient, status, time, optional subject, raw query, and fuzzy message ID matching.
Inputs:
- full
sg_message_id from SendGrid Email Logs for the direct by-ID path, or any useful Email Logs search filters such as recipient, sender, sent time, subject, status, start/end time, raw query, and optional Mail Send message_id for the search-then-detail path
- SendGrid API key from
--api-key, SENDGRID_API_KEY, or the locally saved SendGrid key
Output:
- raw SendGrid JSON by default
- optional
event_summary added by the script, including booleans such as opened, clicked, delivered, bounced, deferred, dropped, spam_reported, and unsubscribed
log_resolution when get_email_log.sh resolves the full sg_message_id before fetching details
matched_messages, matched_count, and _match_reason when using query_email_logs.sh directly with --message-id
- optional saved JSON file when
--output is provided
- dry-run request metadata when
--dry-run is used
This is a read-only lookup. It does not send email and does not retrieve account-level aggregate stats.
What This Skill Needs
- full
sg_message_id, or recipient and sent-time search criteria for Email Logs
- a SendGrid API key, usually
SENDGRID_API_KEY
curl
bash
python3
API key resolution order:
--api-key for the current run
SENDGRID_API_KEY
- locally saved key at
~/.config/sendgrid-send-email/api_key
The saved key path intentionally matches sendgrid-send-email so users who configured the SendGrid send skill can reuse the same key. If no API key is available and the script is running interactively, prompt once and save it locally for future runs. If no API key is available in a non-interactive run, guide the user to read the SendGrid integration guide: https://frevana.gitbook.io/frevana-docs/email-integrations/sendgrid-integration.
Users can update the saved API key with:
bash <skill-path>/scripts/get_email_log.sh --api-key "SG..." --save-api-key
or clear it with:
bash <skill-path>/scripts/get_email_log.sh --clear-api-key
Execution Order
- Extract full
sg_message_id for direct by-ID lookups. If the user only has the Mail Send x-message-id, has an incomplete ID, or does not know the ID, collect as many search filters as available (to, from, sent_at, subject, status, start_time, end_time, or raw query) and let get_email_log.sh resolve the full sg_message_id first.
- Prefer
scripts/get_email_log.sh or scripts/query_email_logs.sh over ad hoc curl.
- Use
--dry-run when the user wants to inspect the URL before calling SendGrid.
- Let the script use
--api-key, SENDGRID_API_KEY, or the locally saved key.
- In non-interactive agent runs, fail fast if the API key is missing from all supported sources.
- Return raw JSON when requested; otherwise summarize the
event_summary, matched messages, or recent activity events.
Commands
Check one message
bash <skill-path>/scripts/get_email_log.sh \
--sg-message-id "abc123.recvd-xxxxxxxx"
Check whether it opened or clicked
bash <skill-path>/scripts/get_email_log.sh \
--sg-message-id "abc123.recvd-xxxxxxxx" \
--summary-only
Search Email Logs by recipient, sent time, and fuzzy message ID
Use this when the user has the x-message-id returned by Mail Send but needs to locate the Email Logs sg_message_id first. get_email_log.sh narrows Email Logs with to_email and sg_message_id_created_at >= (--sent-at - 5 seconds) by default, optionally adds an exact subject filter, fuzzy-matches returned messages[].sg_message_id against the Mail Send response x-message-id, then fetches the detail record with GET /v3/logs/{sg_message_id}.
If the user does not know any message ID, omit --message-id; the script uses the first returned Email Logs message after applying the provided filters, so pass more filters to avoid ambiguity.
For dynamic template sends, omit --subject because the template can define or override the final email subject.
bash <skill-path>/scripts/get_email_log.sh \
--to "customer@example.com" \
--sent-at "2026-06-15T10:37:21Z" \
--message-id "abc123..." \
--limit 100
Search Email Logs without a message ID, then fetch details
bash <skill-path>/scripts/get_email_log.sh \
--to "customer@example.com" \
--from "sender@example.com" \
--start-time "2026-06-15T10:00:00Z" \
--end-time "2026-06-15T11:00:00Z" \
--subject "Order update" \
--status delivered \
--limit 20
Search Email Logs with a raw query
Use this lower-level script when the user only wants search results from POST /v3/logs and does not need the detail endpoint.
bash <skill-path>/scripts/query_email_logs.sh \
--query "to_email = 'customer@example.com' AND status IN ('delivered', 'processed')" \
--limit 10
Parent account querying a subuser or customer account
bash <skill-path>/scripts/get_email_log.sh \
--sg-message-id "abc123.recvd-xxxxxxxx" \
--on-behalf-of "subuser-username"
For customer accounts, pass the full header value, such as:
--on-behalf-of "account-id 123456"
Dry-run request metadata
bash <skill-path>/scripts/get_email_log.sh \
--sg-message-id "abc123.recvd-xxxxxxxx" \
--dry-run
Fixed Request Shape
For by-ID activity lookup, the script sends a GET request to:
- global region:
https://api.sendgrid.com/v3/logs/{sg_message_id}
- EU region:
https://api.eu.sendgrid.com/v3/logs/{sg_message_id}
Supported headers:
Authorization: Bearer <SENDGRID_API_KEY>
- optional
on-behalf-of: <subuser-username> or on-behalf-of: account-id <account-id>
Do not invent query parameters. This endpoint reads one message by ID through the URL path.
For search lookup, the script sends a POST request to:
- global region:
https://api.sendgrid.com/v3/logs
- EU region:
https://api.eu.sendgrid.com/v3/logs
Supported body fields:
query
limit
- optional
subusers
Do not use custom_args.business_id for Email Logs queries. Use recipient, optional subject, --sent-at, and message ID fuzzy matching, or pass a raw account-supported --query.
Script Options
--sg-message-id ID; full SendGrid Email Logs ID for direct activity retrieval
--to EMAIL; recipient email used to resolve the full sg_message_id
--from EMAIL; sender email used to resolve the full sg_message_id
--sent-at ISO_TIME; sent/queued time used as the lower bound for resolving the full sg_message_id
--message-id ID; optional Mail Send x-message-id used for fuzzy matching during resolution
--subject SUBJECT; optional exact subject filter during resolution
--status STATUS; optional status filter, repeatable or comma-separated
--start-time ISO_TIME; optional lower bound for resolution search
--end-time ISO_TIME; optional upper bound for resolution search
--query QUERY; raw SendGrid Email Logs query string for resolution search, overriding generated filters
--sent-at-lookback-seconds N; default 5, allowed 0-300
--limit N; search-result limit during resolution, default 100, allowed 1-1000
--on-behalf-of VALUE; optional SendGrid parent-account header value
--subuser ID; optional numeric SendGrid subuser ID for POST /v3/logs search, repeatable or comma-separated
--region global|eu; default global
--api-key KEY for a one-time override
--save-api-key to save the --api-key value for future runs
--configure-api-key to prompt for and save the key
--clear-api-key to remove the locally saved key
--output PATH to save JSON
--summary-only to print only the computed event summary
--dry-run to print request metadata without calling SendGrid
scripts/query_email_logs.sh supports:
--query QUERY; raw SendGrid Email Logs query string
--status STATUS; repeatable or comma-separated
--to EMAIL
--sent-at ISO_TIME
--sent-at-lookback-seconds N; default 5, allowed 0-300
--message-id ID; fuzzy-matches returned sg_message_id
--subject SUBJECT
--start-time ISO_TIME
--end-time ISO_TIME
--limit N; default 10, allowed 1-1000
--subuser ID
- shared
--region, --api-key, --save-api-key, --configure-api-key, --clear-api-key, --output, and --dry-run
Notes
- Never echo or store the API key in user-visible output.
- The script can save the API key locally at
~/.config/sendgrid-send-email/api_key with file permission 600; do not print the key value back to the user.
- Use
--api-key <key> --save-api-key or --configure-api-key when the user wants to update the saved key. Use --clear-api-key when the user wants to remove it.
- If the user only has a Mail Send
x-message-id, the send result may be incomplete for GET /v3/logs/{sg_message_id}. Prefer scripts/get_email_log.sh --to ... --sent-at ... --message-id ... so the script first resolves the full Email Logs sg_message_id with POST /v3/logs, then fetches details.
- If the user does not know any message ID, use
scripts/get_email_log.sh with multiple filters such as --to, --from, --start-time, --end-time, --subject, and --status; the script picks the first matching Email Logs message, so use narrow filters.
- If
query_email_logs.sh returns no data or no fuzzy sg_message_id match, direct the user to https://app.sendgrid.com/email_logs for manual review.
- If
--api-key, SENDGRID_API_KEY, and the locally saved key are all missing, tell the user to read https://frevana.gitbook.io/frevana-docs/email-integrations/sendgrid-integration to get the required configuration.
- If a user shares an API key in chat, advise them to rotate it.
Example Prompts
Chinese
- "查一下这封 SendGrid 邮件有没有打开或点击,sg_message_id 是 ..."
- "获取这个 SendGrid message id 的事件 timeline"
- "看下这封邮件是否 delivered / opened / clicked"
English
- "Check whether this SendGrid email was opened or clicked"
- "Retrieve SendGrid Email Logs activity for this sg_message_id"
- "Get the event timeline for this SendGrid message"
1---2name: sendgrid-email-log3description: Use when the user wants to query Twilio SendGrid Email Logs or retrieve per-message activity for a specific sg_message_id, including whether an email was opened, clicked, delivered, bounced, deferred, dropped, spam-reported, or unsubscribed. Supports GET /v3/logs/{sg_message_id} by ID and POST /v3/logs search by recipient, optional subject, sent-at lower bound, status, raw query, and fuzzy message ID matching. Supports global or EU SendGrid API base URLs, optional on-behalf-of parent-account header for by-ID lookups, raw JSON output, event summary enrichment, dry-run URL or payload preview, and saved SendGrid API key reuse. Do not use for sending email or aggregate SendGrid statistics.4---56# SendGrid Email Log78Retrieve activity for a specific SendGrid message with the Email Logs API.910## Purpose1112This skill is for **reading SendGrid Email Logs and per-message activity**.1314It has two lookup paths:1516- `scripts/get_email_log.sh` calls `GET /v3/logs/{sg_message_id}` for one specific message and computes an `event_summary`. If the full `sg_message_id` is not known, it can first call `POST /v3/logs` with recipient and sent time to resolve the full ID, then automatically call the detail endpoint.17- `scripts/query_email_logs.sh` calls `POST /v3/logs` to search Email Logs by recipient, status, time, optional subject, raw query, and fuzzy message ID matching.1819Inputs:2021- full `sg_message_id` from SendGrid Email Logs for the direct by-ID path, or any useful Email Logs search filters such as recipient, sender, sent time, subject, status, start/end time, raw query, and optional Mail Send `message_id` for the search-then-detail path22- SendGrid API key from `--api-key`, `SENDGRID_API_KEY`, or the locally saved SendGrid key2324Output:2526- raw SendGrid JSON by default27- optional `event_summary` added by the script, including booleans such as `opened`, `clicked`, `delivered`, `bounced`, `deferred`, `dropped`, `spam_reported`, and `unsubscribed`28- `log_resolution` when `get_email_log.sh` resolves the full `sg_message_id` before fetching details29- `matched_messages`, `matched_count`, and `_match_reason` when using `query_email_logs.sh` directly with `--message-id`30- optional saved JSON file when `--output` is provided31- dry-run request metadata when `--dry-run` is used3233This is a read-only lookup. It does not send email and does not retrieve account-level aggregate stats.3435## What This Skill Needs3637- full `sg_message_id`, or recipient and sent-time search criteria for Email Logs38- a SendGrid API key, usually `SENDGRID_API_KEY`39- `curl`40- `bash`41- `python3`4243API key resolution order:44451. `--api-key` for the current run462. `SENDGRID_API_KEY`473. locally saved key at `~/.config/sendgrid-send-email/api_key`4849The saved key path intentionally matches `sendgrid-send-email` so users who configured the SendGrid send skill can reuse the same key. If no API key is available and the script is running interactively, prompt once and save it locally for future runs. If no API key is available in a non-interactive run, guide the user to read the SendGrid integration guide: <https://frevana.gitbook.io/frevana-docs/email-integrations/sendgrid-integration>.5051Users can update the saved API key with:5253```bash54bash <skill-path>/scripts/get_email_log.sh --api-key "SG..." --save-api-key55```5657or clear it with:5859```bash60bash <skill-path>/scripts/get_email_log.sh --clear-api-key61```6263## Execution Order64651. Extract full `sg_message_id` for direct by-ID lookups. If the user only has the Mail Send `x-message-id`, has an incomplete ID, or does not know the ID, collect as many search filters as available (`to`, `from`, `sent_at`, `subject`, `status`, `start_time`, `end_time`, or raw `query`) and let `get_email_log.sh` resolve the full `sg_message_id` first.662. Prefer `scripts/get_email_log.sh` or `scripts/query_email_logs.sh` over ad hoc `curl`.673. Use `--dry-run` when the user wants to inspect the URL before calling SendGrid.684. Let the script use `--api-key`, `SENDGRID_API_KEY`, or the locally saved key.695. In non-interactive agent runs, fail fast if the API key is missing from all supported sources.706. Return raw JSON when requested; otherwise summarize the `event_summary`, matched messages, or recent activity events.7172## Commands7374### Check one message7576```bash77bash <skill-path>/scripts/get_email_log.sh \78 --sg-message-id "abc123.recvd-xxxxxxxx"79```8081### Check whether it opened or clicked8283```bash84bash <skill-path>/scripts/get_email_log.sh \85 --sg-message-id "abc123.recvd-xxxxxxxx" \86 --summary-only87```8889### Search Email Logs by recipient, sent time, and fuzzy message ID9091Use this when the user has the `x-message-id` returned by Mail Send but needs to locate the Email Logs `sg_message_id` first. `get_email_log.sh` narrows Email Logs with `to_email` and `sg_message_id_created_at >= (--sent-at - 5 seconds)` by default, optionally adds an exact `subject` filter, fuzzy-matches returned `messages[].sg_message_id` against the Mail Send response `x-message-id`, then fetches the detail record with `GET /v3/logs/{sg_message_id}`.9293If the user does not know any message ID, omit `--message-id`; the script uses the first returned Email Logs message after applying the provided filters, so pass more filters to avoid ambiguity.9495For dynamic template sends, omit `--subject` because the template can define or override the final email subject.9697```bash98bash <skill-path>/scripts/get_email_log.sh \99 --to "customer@example.com" \100 --sent-at "2026-06-15T10:37:21Z" \101 --message-id "abc123..." \102 --limit 100103```104105### Search Email Logs without a message ID, then fetch details106107```bash108bash <skill-path>/scripts/get_email_log.sh \109 --to "customer@example.com" \110 --from "sender@example.com" \111 --start-time "2026-06-15T10:00:00Z" \112 --end-time "2026-06-15T11:00:00Z" \113 --subject "Order update" \114 --status delivered \115 --limit 20116```117118### Search Email Logs with a raw query119120Use this lower-level script when the user only wants search results from `POST /v3/logs` and does not need the detail endpoint.121122```bash123bash <skill-path>/scripts/query_email_logs.sh \124 --query "to_email = 'customer@example.com' AND status IN ('delivered', 'processed')" \125 --limit 10126```127128### Parent account querying a subuser or customer account129130```bash131bash <skill-path>/scripts/get_email_log.sh \132 --sg-message-id "abc123.recvd-xxxxxxxx" \133 --on-behalf-of "subuser-username"134```135136For customer accounts, pass the full header value, such as:137138```bash139--on-behalf-of "account-id 123456"140```141142### Dry-run request metadata143144```bash145bash <skill-path>/scripts/get_email_log.sh \146 --sg-message-id "abc123.recvd-xxxxxxxx" \147 --dry-run148```149150## Fixed Request Shape151152For by-ID activity lookup, the script sends a `GET` request to:153154- global region: `https://api.sendgrid.com/v3/logs/{sg_message_id}`155- EU region: `https://api.eu.sendgrid.com/v3/logs/{sg_message_id}`156157Supported headers:158159- `Authorization: Bearer <SENDGRID_API_KEY>`160- optional `on-behalf-of: <subuser-username>` or `on-behalf-of: account-id <account-id>`161162Do not invent query parameters. This endpoint reads one message by ID through the URL path.163164For search lookup, the script sends a `POST` request to:165166- global region: `https://api.sendgrid.com/v3/logs`167- EU region: `https://api.eu.sendgrid.com/v3/logs`168169Supported body fields:170171- `query`172- `limit`173- optional `subusers`174175Do not use `custom_args.business_id` for Email Logs queries. Use recipient, optional subject, `--sent-at`, and message ID fuzzy matching, or pass a raw account-supported `--query`.176177## Script Options178179- `--sg-message-id ID`; full SendGrid Email Logs ID for direct activity retrieval180- `--to EMAIL`; recipient email used to resolve the full `sg_message_id`181- `--from EMAIL`; sender email used to resolve the full `sg_message_id`182- `--sent-at ISO_TIME`; sent/queued time used as the lower bound for resolving the full `sg_message_id`183- `--message-id ID`; optional Mail Send `x-message-id` used for fuzzy matching during resolution184- `--subject SUBJECT`; optional exact subject filter during resolution185- `--status STATUS`; optional status filter, repeatable or comma-separated186- `--start-time ISO_TIME`; optional lower bound for resolution search187- `--end-time ISO_TIME`; optional upper bound for resolution search188- `--query QUERY`; raw SendGrid Email Logs query string for resolution search, overriding generated filters189- `--sent-at-lookback-seconds N`; default `5`, allowed `0-300`190- `--limit N`; search-result limit during resolution, default `100`, allowed `1-1000`191- `--on-behalf-of VALUE`; optional SendGrid parent-account header value192- `--subuser ID`; optional numeric SendGrid subuser ID for `POST /v3/logs` search, repeatable or comma-separated193- `--region global|eu`; default `global`194- `--api-key KEY` for a one-time override195- `--save-api-key` to save the `--api-key` value for future runs196- `--configure-api-key` to prompt for and save the key197- `--clear-api-key` to remove the locally saved key198- `--output PATH` to save JSON199- `--summary-only` to print only the computed event summary200- `--dry-run` to print request metadata without calling SendGrid201202`scripts/query_email_logs.sh` supports:203204- `--query QUERY`; raw SendGrid Email Logs query string205- `--status STATUS`; repeatable or comma-separated206- `--to EMAIL`207- `--sent-at ISO_TIME`208- `--sent-at-lookback-seconds N`; default `5`, allowed `0-300`209- `--message-id ID`; fuzzy-matches returned `sg_message_id`210- `--subject SUBJECT`211- `--start-time ISO_TIME`212- `--end-time ISO_TIME`213- `--limit N`; default `10`, allowed `1-1000`214- `--subuser ID`215- shared `--region`, `--api-key`, `--save-api-key`, `--configure-api-key`, `--clear-api-key`, `--output`, and `--dry-run`216217## Notes218219- Never echo or store the API key in user-visible output.220- The script can save the API key locally at `~/.config/sendgrid-send-email/api_key` with file permission `600`; do not print the key value back to the user.221- Use `--api-key <key> --save-api-key` or `--configure-api-key` when the user wants to update the saved key. Use `--clear-api-key` when the user wants to remove it.222- If the user only has a Mail Send `x-message-id`, the send result may be incomplete for `GET /v3/logs/{sg_message_id}`. Prefer `scripts/get_email_log.sh --to ... --sent-at ... --message-id ...` so the script first resolves the full Email Logs `sg_message_id` with `POST /v3/logs`, then fetches details.223- If the user does not know any message ID, use `scripts/get_email_log.sh` with multiple filters such as `--to`, `--from`, `--start-time`, `--end-time`, `--subject`, and `--status`; the script picks the first matching Email Logs message, so use narrow filters.224- If `query_email_logs.sh` returns no data or no fuzzy `sg_message_id` match, direct the user to <https://app.sendgrid.com/email_logs> for manual review.225- If `--api-key`, `SENDGRID_API_KEY`, and the locally saved key are all missing, tell the user to read <https://frevana.gitbook.io/frevana-docs/email-integrations/sendgrid-integration> to get the required configuration.226- If a user shares an API key in chat, advise them to rotate it.227228## Example Prompts229230### Chinese231232- "查一下这封 SendGrid 邮件有没有打开或点击,sg_message_id 是 ..."233- "获取这个 SendGrid message id 的事件 timeline"234- "看下这封邮件是否 delivered / opened / clicked"235236### English237238- "Check whether this SendGrid email was opened or clicked"239- "Retrieve SendGrid Email Logs activity for this sg_message_id"240- "Get the event timeline for this SendGrid message"