sendgridSendGrid API integration with managed OAuth. Send emails, manage contacts, templates, suppressions, and view email statistics. Use this skill when users want to send transactional or marketing emails, manage email lists, handle bounces/unsubscribes, or analyze email performance. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
Install via ClawdBot CLI:
clawdbot install byungkyu/sendgridAccess the SendGrid API with managed OAuth authentication. Send transactional and marketing emails, manage contacts, templates, suppressions, and analyze email performance.
# Get user profile
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/sendgrid/v3/user/profile')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
https://gateway.maton.ai/sendgrid/{native-api-path}
Replace {native-api-path} with the actual SendGrid API endpoint path. The gateway proxies requests to api.sendgrid.com and automatically injects your OAuth token.
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Manage your SendGrid OAuth connections at https://ctrl.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=sendgrid&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'sendgrid'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "943c6cd5-9a56-4f5b-8adf-ecd4a140049f",
"status": "ACTIVE",
"creation_time": "2026-02-11T10:53:41.817938Z",
"last_updated_time": "2026-02-11T10:54:05.554084Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "sendgrid",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple SendGrid connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/sendgrid/v3/user/profile')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '943c6cd5-9a56-4f5b-8adf-ecd4a140049f')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
All SendGrid API endpoints follow this pattern:
/sendgrid/v3/{resource}
POST /sendgrid/v3/mail/send
Content-Type: application/json
{
"personalizations": [
{
"to": [{"email": "recipient@example.com", "name": "Recipient"}],
"subject": "Hello from SendGrid"
}
],
"from": {"email": "sender@example.com", "name": "Sender"},
"content": [
{
"type": "text/plain",
"value": "This is a test email."
}
]
}
With HTML content:
POST /sendgrid/v3/mail/send
Content-Type: application/json
{
"personalizations": [
{
"to": [{"email": "recipient@example.com"}],
"subject": "HTML Email"
}
],
"from": {"email": "sender@example.com"},
"content": [
{
"type": "text/html",
"value": "<h1>Hello</h1><p>This is an HTML email.</p>"
}
]
}
With template:
POST /sendgrid/v3/mail/send
Content-Type: application/json
{
"personalizations": [
{
"to": [{"email": "recipient@example.com"}],
"dynamic_template_data": {
"first_name": "John",
"order_id": "12345"
}
}
],
"from": {"email": "sender@example.com"},
"template_id": "d-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
GET /sendgrid/v3/user/profile
Response:
{
"type": "user",
"userid": 59796657
}
GET /sendgrid/v3/user/account
GET /sendgrid/v3/marketing/contacts
Response:
{
"result": [],
"contact_count": 0,
"_metadata": {
"self": "https://api.sendgrid.com/v3/marketing/contacts"
}
}
POST /sendgrid/v3/marketing/contacts/search
Content-Type: application/json
{
"query": "email LIKE '%@example.com%'"
}
PUT /sendgrid/v3/marketing/contacts
Content-Type: application/json
{
"contacts": [
{
"email": "contact@example.com",
"first_name": "John",
"last_name": "Doe"
}
]
}
Response:
{
"job_id": "2387e363-4104-4225-8960-4a5758492351"
}
Note: Contact operations are asynchronous. Use the job status endpoint to check progress.
GET /sendgrid/v3/marketing/contacts/imports/{job_id}
Response:
{
"id": "2387e363-4104-4225-8960-4a5758492351",
"status": "pending",
"job_type": "upsert_contacts",
"results": {
"requested_count": 1,
"created_count": 1
},
"started_at": "2026-02-11T11:00:14Z"
}
DELETE /sendgrid/v3/marketing/contacts?ids=contact_id_1,contact_id_2
GET /sendgrid/v3/marketing/contacts/{contact_id}
POST /sendgrid/v3/marketing/contacts/search/emails
Content-Type: application/json
{
"emails": ["contact@example.com"]
}
GET /sendgrid/v3/marketing/lists
Response:
{
"result": [],
"_metadata": {
"self": "https://api.sendgrid.com/v3/marketing/lists?page_size=100&page_token="
}
}
POST /sendgrid/v3/marketing/lists
Content-Type: application/json
{
"name": "My Contact List"
}
Response:
{
"name": "My Contact List",
"id": "b050f139-4231-47c8-bf32-94ad76376d3b",
"contact_count": 0,
"_metadata": {
"self": "https://api.sendgrid.com/v3/marketing/lists/b050f139-4231-47c8-bf32-94ad76376d3b"
}
}
GET /sendgrid/v3/marketing/lists/{list_id}
PATCH /sendgrid/v3/marketing/lists/{list_id}
Content-Type: application/json
{
"name": "Updated List Name"
}
DELETE /sendgrid/v3/marketing/lists/{list_id}
PUT /sendgrid/v3/marketing/contacts
Content-Type: application/json
{
"list_ids": ["list_id"],
"contacts": [
{"email": "contact@example.com"}
]
}
GET /sendgrid/v3/marketing/segments
POST /sendgrid/v3/marketing/segments
Content-Type: application/json
{
"name": "Active Users",
"query_dsl": "email_clicks > 0"
}
GET /sendgrid/v3/marketing/segments/{segment_id}
DELETE /sendgrid/v3/marketing/segments/{segment_id}
GET /sendgrid/v3/templates
With generation filter:
GET /sendgrid/v3/templates?generations=dynamic
POST /sendgrid/v3/templates
Content-Type: application/json
{
"name": "My Template",
"generation": "dynamic"
}
Response:
{
"id": "d-ffcdb43ed8a04beba48a702e1717ddb5",
"name": "My Template",
"generation": "dynamic",
"updated_at": "2026-02-11 11:00:20",
"versions": []
}
GET /sendgrid/v3/templates/{template_id}
PATCH /sendgrid/v3/templates/{template_id}
Content-Type: application/json
{
"name": "Updated Template Name"
}
DELETE /sendgrid/v3/templates/{template_id}
POST /sendgrid/v3/templates/{template_id}/versions
Content-Type: application/json
{
"name": "Version 1",
"subject": "{{subject}}",
"html_content": "<html><body><h1>Hello {{name}}</h1></body></html>",
"active": 1
}
Response:
{
"id": "54230a99-1e89-4edf-821d-d4925b40c64b",
"template_id": "d-ffcdb43ed8a04beba48a702e1717ddb5",
"active": 1,
"name": "Version 1",
"html_content": "<html><body><h1>Hello {{name}}</h1></body></html>",
"plain_content": "Hello {{name}}",
"generate_plain_content": true,
"subject": "{{subject}}",
"editor": "code",
"thumbnail_url": "//..."
}
GET /sendgrid/v3/senders
POST /sendgrid/v3/senders
Content-Type: application/json
{
"nickname": "My Sender",
"from": {"email": "sender@example.com", "name": "Sender Name"},
"reply_to": {"email": "reply@example.com", "name": "Reply To"},
"address": "123 Main St",
"city": "San Francisco",
"country": "USA"
}
Response:
{
"id": 8513177,
"nickname": "My Sender",
"from": {"email": "sender@example.com", "name": "Sender Name"},
"reply_to": {"email": "reply@example.com", "name": "Reply To"},
"address": "123 Main St",
"city": "San Francisco",
"country": "USA",
"verified": {"status": false, "reason": null},
"updated_at": 1770786031,
"created_at": 1770786031,
"locked": false
}
Note: Sender verification is required before use. Check verified.status.
GET /sendgrid/v3/senders/{sender_id}
PATCH /sendgrid/v3/senders/{sender_id}
Content-Type: application/json
{
"nickname": "Updated Sender Name"
}
DELETE /sendgrid/v3/senders/{sender_id}
# List bounces
GET /sendgrid/v3/suppression/bounces
# Get bounce by email
GET /sendgrid/v3/suppression/bounces/{email}
# Delete bounces
DELETE /sendgrid/v3/suppression/bounces
Content-Type: application/json
{
"emails": ["bounce@example.com"]
}
# List blocks
GET /sendgrid/v3/suppression/blocks
# Get block by email
GET /sendgrid/v3/suppression/blocks/{email}
# Delete blocks
DELETE /sendgrid/v3/suppression/blocks
Content-Type: application/json
{
"emails": ["blocked@example.com"]
}
# List invalid emails
GET /sendgrid/v3/suppression/invalid_emails
# Delete invalid emails
DELETE /sendgrid/v3/suppression/invalid_emails
Content-Type: application/json
{
"emails": ["invalid@example.com"]
}
# List spam reports
GET /sendgrid/v3/suppression/spam_reports
# Delete spam reports
DELETE /sendgrid/v3/suppression/spam_reports
Content-Type: application/json
{
"emails": ["spam@example.com"]
}
# List global unsubscribes
GET /sendgrid/v3/suppression/unsubscribes
# Add to global unsubscribes
POST /sendgrid/v3/asm/suppressions/global
Content-Type: application/json
{
"recipient_emails": ["unsubscribe@example.com"]
}
GET /sendgrid/v3/asm/groups
POST /sendgrid/v3/asm/groups
Content-Type: application/json
{
"name": "Weekly Newsletter",
"description": "Weekly newsletter updates"
}
Response:
{
"name": "Weekly Newsletter",
"id": 122741,
"description": "Weekly newsletter updates",
"is_default": false
}
GET /sendgrid/v3/asm/groups/{group_id}
PATCH /sendgrid/v3/asm/groups/{group_id}
Content-Type: application/json
{
"name": "Updated Group Name"
}
DELETE /sendgrid/v3/asm/groups/{group_id}
POST /sendgrid/v3/asm/groups/{group_id}/suppressions
Content-Type: application/json
{
"recipient_emails": ["user@example.com"]
}
GET /sendgrid/v3/asm/groups/{group_id}/suppressions
GET /sendgrid/v3/stats?start_date=2026-02-01
With end date:
GET /sendgrid/v3/stats?start_date=2026-02-01&end_date=2026-02-28
Response:
[
{
"date": "2026-02-01",
"stats": [
{
"metrics": {
"blocks": 0,
"bounce_drops": 0,
"bounces": 0,
"clicks": 0,
"deferred": 0,
"delivered": 0,
"invalid_emails": 0,
"opens": 0,
"processed": 0,
"requests": 0,
"spam_report_drops": 0,
"spam_reports": 0,
"unique_clicks": 0,
"unique_opens": 0,
"unsubscribe_drops": 0,
"unsubscribes": 0
}
}
]
}
]
GET /sendgrid/v3/categories/stats?start_date=2026-02-01&categories=category1,category2
GET /sendgrid/v3/mailbox_providers/stats?start_date=2026-02-01
GET /sendgrid/v3/browsers/stats?start_date=2026-02-01
GET /sendgrid/v3/api_keys
Response:
{
"result": [
{
"name": "MatonTest",
"api_key_id": "WJBgv5EKR8y0nn2F8Qfk5w"
}
]
}
POST /sendgrid/v3/api_keys
Content-Type: application/json
{
"name": "New API Key",
"scopes": ["mail.send", "alerts.read"]
}
GET /sendgrid/v3/api_keys/{api_key_id}
PATCH /sendgrid/v3/api_keys/{api_key_id}
Content-Type: application/json
{
"name": "Updated Key Name"
}
DELETE /sendgrid/v3/api_keys/{api_key_id}
SendGrid uses token-based pagination for marketing endpoints:
GET /sendgrid/v3/marketing/lists?page_size=100&page_token={token}
Response includes:
{
"result": [...],
"_metadata": {
"self": "https://api.sendgrid.com/v3/marketing/lists?page_size=100&page_token=",
"next": "https://api.sendgrid.com/v3/marketing/lists?page_size=100&page_token=abc123"
}
}
For suppression endpoints, use limit and offset:
GET /sendgrid/v3/suppression/bounces?limit=100&offset=0
// Send an email
const response = await fetch(
'https://gateway.maton.ai/sendgrid/v3/mail/send',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
personalizations: [{
to: [{email: 'recipient@example.com'}],
subject: 'Hello'
}],
from: {email: 'sender@example.com'},
content: [{type: 'text/plain', value: 'Hello World'}]
})
}
);
import os
import requests
# Get email stats
response = requests.get(
'https://gateway.maton.ai/sendgrid/v3/stats',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
params={'start_date': '2026-02-01'}
)
data = response.json()
for day in data:
metrics = day['stats'][0]['metrics']
print(f"{day['date']}: {metrics['delivered']} delivered, {metrics['opens']} opens")
d-start_time and end_time (Unix timestamps)curl -g when URLs contain brackets to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| Status | Meaning |
|--------|---------|
| 400 | Bad request or validation error |
| 401 | Invalid or missing Maton API key |
| 403 | Insufficient permissions |
| 404 | Resource not found |
| 429 | Rate limited |
| 500 | Internal server error |
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
sendgrid. For example:https://gateway.maton.ai/sendgrid/v3/user/profilehttps://gateway.maton.ai/v3/user/profileAI Usage Analysis
Analysis is being generated… refresh in a few seconds.
Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects Clau...
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
Search and analyze your own session logs (older/parent conversations) using jq.
Typed knowledge graph for structured agent memory and composable skills. Use when creating/querying entities (Person, Project, Task, Event, Document), linking related objects, enforcing constraints, planning multi-step actions as graph transformations, or when skills need to share state. Trigger on "remember", "what do I know about", "link X to Y", "show dependencies", entity CRUD, or cross-skill data access.
Ultimate AI agent memory system for Cursor, Claude, ChatGPT & Copilot. WAL protocol + vector search + git-notes + cloud backup. Never lose context again. Vibe-coding ready.
Headless browser automation CLI optimized for AI agents with accessibility tree snapshots and ref-based element selection