mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
3256a87bcb
Adds five top-level operational tool skills, one per named tool: - slack: messages, channels, threads, search, files, and webhook signature verification (HMAC-SHA256) via a bounded, stdlib-only slack-cli. - notion: pages, database queries, search, and guarded page updates via notion-cli. - email: transactional email via Twilio SendGrid (send, deliverability bounces/spam reports, Signed Event Webhook verification with a self-contained ECDSA P-256 verifier) via email-cli. - crm: HubSpot CRM records, contact search, and deal pipeline views with guarded stage updates via crm-cli. - stripe: read-only-first balance, payment, and subscription queries with a guarded period-end subscription cancellation via stripe-cli. Each skill ships an executable script (--json output, --limit bounded reads, --dry-run/--yes mutation gate), a human README with the five required sections, a schema-v1 evals/evals.json with six output-quality cases, a dated source index + operations reference, and a deterministic unittest suite run by check-artifacts. All five are indexed in the top-level README and the generated catalogs were regenerated. Eval coverage rises from 78/139 to 83/144. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
4.1 KiB
4.1 KiB
SendGrid Operations
Last Updated: 2026-08-03
Operational detail for the Twilio SendGrid surface the skill owns: the Mail Send API, suppression endpoints for deliverability, and Signed Event Webhook verification. The bundled email-cli implements this reference; use this document when a call behaves unexpectedly.
API conventions
- Base URL:
https://api.sendgrid.com/v3. Every request carriesAuthorization: Bearer <API_KEY>and JSON bodies. - The API key scope matters:
mail.sendis needed to send; suppression reads need access to the Suppression endpoints (suppression.read/suppression access on the key). An unauthorized call returns 401 with an error body. email-clihonorsSENDGRID_API_BASE(test/stub override); production default is the v3 base.
Sending (POST /v3/mail/send)
- Payload:
{"from": {"email": ...}, "personalizations": [{"to": [{"email": ...}], "subject": ...}], "content": [{"type": "text/plain", "value": ...}]}plus an optionaltext/htmlcontent part. - The from-address must be a verified sender identity on the account; unverified senders are rejected.
- Success is HTTP 202 Accepted with an empty body and the message id in the
X-Message-Idresponse header — the email is queued, not delivered. Acceptance ≠ delivery; confirm with bounce events, spam reports, or the activity feed. - Failure is an HTTP 4xx/5xx with an
errorsarray naming the field (e.g. invalidfrom, missing content).
Deliverability surface (read-only)
| Signal | Endpoint | What it means |
|---|---|---|
| Bounces | GET /suppression/bounces?limit=N |
Recipients whose mail bounced, with reason, status, created |
| Spam reports | GET /suppression/spam_reports?limit=N |
Recipients who marked mail as spam, with ip and reason |
- Suppression endpoints return plain arrays (not paginated objects);
limitcaps them (max 500). Bounded-read rule: request only what the task needs;email-cli --limitcaps at the request level. - Hard bounces (permanent, 5.x.x) indicate invalid addresses — do not re-send to them. Spam complaints indicate content or targeting problems — review before the next send.
Signed Event Webhook verification
SendGrid signs each webhook POST when "Signed Webhook" is enabled. Algorithm (mirrors the official sendgrid-python EventWebhook.verify_signature helper):
- Raw body bytes: use the exact request body; re-encoding (pretty-print, charset change) breaks verification.
- Timestamp:
X-Twilio-Email-Event-Webhook-Timestamp(Unix seconds). Reject if|now - ts| > 300s(default replay window;--max-age 0disables for historical verification). - Data: SHA-256 digest over
timestamp + raw body— bytes concatenated with no separator (timestamped_payload = (timestamp + payload).encode('utf-8')). - Signature:
X-Twilio-Email-Event-Webhook-Signature, base64-decoded ASN.1 DER(r, s), verified as an ECDSA signature over the P-256 (secp256r1) curve with the webhook public key. - Constant-time semantics: compare by performing the mathematical verification; never string-compare signatures.
The public key is displayed in the Event Webhook settings dialog when Signed Webhook is enabled and can also be fetched via the Event Webhook API. Store it as a PEM file, never in code. email-cli webhook verify implements steps 1–5 with a self-contained stdlib P-256 verifier (no cryptography dependency).
Error handling
- 401
unauthorized: API key invalid or missing scope — rotate/regrant, never retry blindly. - 400
errors[]: payload problem; the message names the offending field (e.g. from-address not verified). - 429
rate_limited: slow down and retry with backoff. email-cliexit 1 withEmail API HTTP <code>: <detail>(human) or{"ok": false, "error": "..."}(JSON). Exit 2 is a usage error.
Credential hygiene
- API keys are full-account credentials: store in
SENDGRID_API_KEY, never in code, chat, or commits. Scope keys narrowly (mail.sendonly, or a separate key for suppression reads) and rotate on leak. - Webhook public keys are public material; webhook secrets do not exist in this scheme (signature verification is the security boundary).