id: gmail_single_user name: Gmail Single User version: 1.0.0 description: "Gmail for a single mailbox over OAuth 2.0 (no service account, no domain-wide delegation) — search/read messages, send and reply (with attachments), retrieve attachments, and ingest messages (get_incidents) with an OCSF mapper. Uses a per-mailbox OAuth refresh token; stdlib-only, no extra Python dependencies." changelog: "1.0.0 — Initial release: OAuth authorization link + code exchange, connectivity test, message ingestion with OCSF mapper, send/reply mail with attachments, and attachment retrieval." category: email # Per-instance configuration. This integration authenticates as ONE Gmail user # via OAuth 2.0 (not a service account). One-time setup: # 1. Create an OAuth client (type: Web application) in Google Cloud, authorize # the Gmail scopes, and note the Client ID / Client Secret. Add the redirect # URI below to the client's authorized redirect URIs. # 2. Run gmail-single-user-auth-link, open the URL, consent, and copy the # "code" value from the redirect. # 3. Run gmail-single-user-exchange-code with that code to get a refresh_token. # 4. Paste the refresh_token into the config below and save. # Every command then exchanges the refresh token for a short-lived access token. config_schema: properties: email: type: string description: "The Gmail address of the mailbox (used as the default From address)" client_id: type: string description: "OAuth client ID" client_secret: type: string description: "OAuth client secret" x-soar-sensitive: true refresh_token: type: string description: "OAuth refresh token (obtain it once with gmail-single-user-exchange-code, then paste it here)" x-soar-sensitive: true redirect_uri: type: string description: "OAuth redirect URI registered on the client (must match at auth-link and exchange-code time)" default: http://localhost send_as: type: string description: "Optional address to send from (a verified send-as alias of the mailbox)" required: - email - client_id - client_secret commands: - id: auth_link name: gmail-single-user-auth-link description: "Generate the OAuth consent URL. Open it, approve access, and copy the returned code for gmail-single-user-exchange-code." risk: read inputs_schema: properties: {} required: [] outputs_schema: { properties: {} } - id: exchange_code name: gmail-single-user-exchange-code description: "Exchange an OAuth authorization code for a refresh token (paste the refresh_token into the instance config afterwards)." inputs_schema: properties: code: { type: string, description: "Authorization code from the consent redirect" } redirect_uri: { type: string, description: "Override the configured redirect URI (must match the one used for auth-link)" } required: [code] outputs_schema: { properties: {} } - id: get_incidents name: gmail-single-user-get-incidents description: "Fetch messages from the mailbox for ingestion. Returns {result:[...]}; use result as the alert rule results path." risk: read inputs_schema: properties: query: { type: string, description: "Extra Gmail search query (same syntax as the Gmail search box)" } after: { type: string, description: "Lower bound on message date, epoch (s/ms) or ISO8601 (incremental fetch watermark)" } subject: { type: string, description: "Filter by subject" } from: { type: string, description: "Filter by sender" } to: { type: string, description: "Filter by recipient" } filename: { type: string, description: "Filter by attachment filename or type" } label_id: { type: string, description: "Restrict to a label ID" } has_attachments: { type: boolean, description: "Only messages with attachments" } max: { type: number, description: "Maximum messages to fetch (default 50)" } required: [] outputs_schema: { properties: {} } ingest: results_path: result dedup_key: id incremental_field: after - id: search name: gmail-single-user-search description: "Search the mailbox and return flattened messages (subject/from/to/body/attachments)." risk: read inputs_schema: properties: query: { type: string, description: "Gmail search query" } subject: { type: string, description: "Filter by subject" } from: { type: string, description: "Filter by sender" } to: { type: string, description: "Filter by recipient" } filename: { type: string, description: "Filter by attachment filename or type" } label_id: { type: string, description: "Restrict to a label ID" } has_attachments: { type: boolean, description: "Only messages with attachments" } max_results: { type: number, description: "Maximum messages (default 100, max 500)" } page_token: { type: string, description: "Page token for the next results page" } include_spam_trash: { type: boolean, description: "Include SPAM and TRASH" } required: [] outputs_schema: { properties: {} } - id: get_mail name: gmail-single-user-get-mail description: "Retrieve a single message (flattened) by ID." risk: read inputs_schema: properties: message_id: { type: string, description: "Message ID" } required: [message_id] outputs_schema: { properties: {} } - id: send_mail name: gmail-single-user-send-mail description: "Send an email from the mailbox (optional single attachment as base64)." inputs_schema: properties: to: { type: string, description: "Recipient(s), comma-separated" } subject: { type: string, description: "Subject" } body: { type: string, description: "Plain-text body" } html_body: { type: string, description: "HTML body" } cc: { type: string, description: "CC recipient(s), comma-separated" } bcc: { type: string, description: "BCC recipient(s), comma-separated" } reply_to: { type: string, description: "Reply-To address" } attachment_name: { type: string, description: "Attachment file name (with attachment_base64)" } attachment_base64: { type: string, description: "Attachment content, base64-encoded" } required: [to, subject] outputs_schema: { properties: {} } - id: reply_mail name: gmail-single-user-reply-mail description: "Reply to a message (sets In-Reply-To/References so it threads correctly)." inputs_schema: properties: to: { type: string, description: "Recipient(s), comma-separated" } subject: { type: string, description: "Subject (keep the original for correct threading)" } in_reply_to: { type: string, description: "Message-ID header of the message being replied to" } references: { type: string, description: "References header (space-separated Message-IDs)" } body: { type: string, description: "Plain-text body" } html_body: { type: string, description: "HTML body" } cc: { type: string, description: "CC recipient(s), comma-separated" } bcc: { type: string, description: "BCC recipient(s), comma-separated" } reply_to: { type: string, description: "Reply-To address" } attachment_name: { type: string, description: "Attachment file name (with attachment_base64)" } attachment_base64: { type: string, description: "Attachment content, base64-encoded" } required: [to, subject, in_reply_to] outputs_schema: { properties: {} } - id: get_attachments name: gmail-single-user-get-attachments description: "Retrieve a message's attachments as base64." risk: read inputs_schema: properties: message_id: { type: string, description: "Message ID" } required: [message_id] outputs_schema: { properties: {} } - id: test_connection name: gmail-single-user-test-connection description: "Verify the OAuth refresh token by reading the mailbox profile (used by the Test button)." risk: read inputs_schema: properties: {} required: [] outputs_schema: { properties: {} } ingestion: command: get_incidents mapper: get_incidents default_incident_type: "Gmail Single User Message"