DevOps & Infrastructure
Send deployment notifications, on-call alerts, and infra change confirmations from your CI/CD pipelines, IaC tools, and container orchestration.
Each integration lives under the integrations/ directory. Drop in your MAILGRID_API_KEY secret, wire up the step or resource, and every deploy, apply, or helm upgrade can notify your team without leaving the pipeline.
GitHub Actions
Located at integrations/github-action/. A native Node 20 action — no external dependencies, just action.yml and a small TypeScript runner that calls the InboxOS API.
action.yml inputs
| Input | Required | Description |
|---|---|---|
api-key | Yes | Your Mailgrid API key — store as a repo secret |
from | Yes | Verified sender address |
to | Yes | Recipient address (comma-separated for multiple) |
subject | Yes | Email subject line |
body | Yes | Plain-text or HTML body |
Complete workflow example
name: Deploy & Notify on: push: branches: [main] release: types: [published] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build & deploy run: npm ci && npm run deploy - name: Notify on success if: success() uses: mailgrid/notify-action@v1 with: api-key: ${{ secrets.MAILGRID_API_KEY }} from: deploys@yourapp.com to: team@yourapp.com subject: "Deployed ${{ github.ref_name }} to production" body: | Deployment succeeded. Commit: ${{ github.sha }} Author: ${{ github.actor }} Workflow: ${{ github.workflow }} - name: Notify on failure if: failure() uses: mailgrid/notify-action@v1 with: api-key: ${{ secrets.MAILGRID_API_KEY }} from: deploys@yourapp.com to: oncall@yourapp.com subject: "FAILED: ${{ github.ref_name }} deployment" body: | Deployment failed — immediate attention required. Commit: ${{ github.sha }} Author: ${{ github.actor }} Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
Trigger on release: published to email your subscriber list every time a GitHub release is cut. Combine with the Templates API to render a proper changelog email.
GitLab CI
Located at integrations/gitlab-ci/. A curl-based job definition — no additional runners or plugins needed. Add the snippet to any .gitlab-ci.yml stage.
stages: - build - deploy - notify notify-deploy: stage: notify image: curlimages/curl:latest when: on_success script: - | curl -s -X POST https://api.mailgrid.space/api/emails \ -H "Authorization: Bearer $MAILGRID_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"from\": \"deploys@yourapp.com\", \"to\": \"team@yourapp.com\", \"subject\": \"Deployed ${CI_COMMIT_REF_NAME} — pipeline #${CI_PIPELINE_ID}\", \"text\": \"Commit ${CI_COMMIT_SHORT_SHA} by ${GITLAB_USER_LOGIN} deployed successfully.\n\nPipeline: ${CI_PIPELINE_URL}\" }" notify-failure: stage: notify image: curlimages/curl:latest when: on_failure script: - | curl -s -X POST https://api.mailgrid.space/api/emails \ -H "Authorization: Bearer $MAILGRID_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"from\": \"deploys@yourapp.com\", \"to\": \"oncall@yourapp.com\", \"subject\": \"FAILED: pipeline #${CI_PIPELINE_ID} on ${CI_COMMIT_REF_NAME}\", \"text\": \"Pipeline failed.\n\nCommit: ${CI_COMMIT_SHORT_SHA}\nAuthor: ${GITLAB_USER_LOGIN}\nURL: ${CI_PIPELINE_URL}\" }"
Set MAILGRID_API_KEY under Settings → CI/CD → Variables with the "Masked" flag so it never appears in job logs.
Netlify
Located at integrations/netlify/. A Netlify Build Plugin that hooks into onSuccess and onError lifecycle events to send deployment emails without any custom functions.
netlify.toml plugin config
[[plugins]]
package = "@mailgrid/netlify-plugin"
[plugins.inputs]
from = "deploys@yourapp.com"
to = "team@yourapp.com"
success_subject = "Netlify deploy succeeded — {{BRANCH}}"
failure_subject = "Netlify deploy FAILED — {{BRANCH}}"
Plugin lifecycle hooks
module.exports = { async onSuccess({ inputs, utils }) { await sendEmail({ apiKey: process.env.MAILGRID_API_KEY, from: inputs.from, to: inputs.to, subject: inputs.success_subject .replace("{{BRANCH}}", process.env.BRANCH), text: [ `Deploy ID: ${process.env.DEPLOY_ID}`, `Branch: ${process.env.BRANCH}`, `URL: ${process.env.DEPLOY_URL}`, `Context: ${process.env.CONTEXT}`, ].join("\n"), }); }, async onError({ inputs, error, utils }) { await sendEmail({ apiKey: process.env.MAILGRID_API_KEY, from: inputs.from, to: inputs.to, subject: inputs.failure_subject .replace("{{BRANCH}}", process.env.BRANCH), text: `Build failed: ${error.message}\n\nDeploy ID: ${process.env.DEPLOY_ID}`, }); }, };
Add MAILGRID_API_KEY as an environment variable in the Netlify UI under Site settings → Environment variables.
Vercel
Located at integrations/vercel/. A Vercel integration manifest that installs directly from the Vercel Marketplace. After installation it automatically injects MAILGRID_API_KEY as an environment variable into every project in your Vercel team.
integration.json
{
"name": "Mailgrid",
"slug": "mailgrid",
"description": "Transactional email via AWS SES for Vercel projects",
"scopes": ["read-write:environment-variables"],
"ui": {
"redirect_login_page": "https://api.mailgrid.space/oauth/vercel/callback"
},
"env": [
{
"key": "MAILGRID_API_KEY",
"description": "Mailgrid API key injected by the integration",
"visible": false
},
{
"key": "MAILGRID_FROM_EMAIL",
"description": "Default verified sender address",
"visible": true
}
]
}
Once installed, call POST https://api.mailgrid.space/api/emails directly from your Vercel API routes using process.env.MAILGRID_API_KEY — no additional configuration needed.
Cloudflare Pages
Located at integrations/cloudflare-pages/. A Pages Functions proxy at /api/mailgrid/[[path]].js. Client-side code calls /api/mailgrid/emails; the Function forwards the request to InboxOS server-side, keeping MAILGRID_API_KEY out of the browser bundle.
export async function onRequest(context) { const { request, env, params } = context; const path = (params.path ?? []).join("/"); const upstream = `https://api.mailgrid.space/api/${path}`; // Forward the request with the server-side API key const proxied = new Request(upstream, { method: request.method, headers: new Headers({ "Authorization": `Bearer ${env.MAILGRID_API_KEY}`, "Content-Type": "application/json", }), body: request.method !== "GET" ? request.body : undefined, }); const response = await fetch(proxied); return new Response(response.body, { status: response.status, headers: { "Content-Type": "application/json" }, }); }
Set MAILGRID_API_KEY in your Pages project under Settings → Environment variables → Production. The proxy accepts the same JSON body as the direct API.
Terraform
Located at integrations/terraform/. Uses the hashicorp/http provider to call the Mailgrid API as part of your infrastructure apply — verifies a domain and sends a test email to confirm the stack is live.
terraform { required_providers { http = { source = "hashicorp/http" version = "~> 3.0" } } } variable "mailgrid_api_key" { type = string sensitive = true } # Verify sending domain resource "terraform_data" "domain_verify" { provisioner "local-exec" { command = <<-EOT curl -s -X POST https://api.mailgrid.space/api/domains/verify \ -H "Authorization: Bearer ${var.mailgrid_api_key}" \ -H "Content-Type: application/json" \ -d '{"domain": "yourapp.com"}' EOT } } # Send post-apply confirmation email resource "terraform_data" "deploy_notification" { depends_on = [terraform_data.domain_verify] provisioner "local-exec" { command = <<-EOT curl -s -X POST https://api.mailgrid.space/api/emails \ -H "Authorization: Bearer ${var.mailgrid_api_key}" \ -H "Content-Type: application/json" \ -d '{ "from": "infra@yourapp.com", "to": "team@yourapp.com", "subject": "Terraform apply complete", "text": "Infrastructure stack applied successfully." }' EOT } }
Pass the API key via TF_VAR_mailgrid_api_key or a .tfvars file — never commit it to version control.
Pulumi
Located at integrations/pulumi/. A TypeScript dynamic resource — MailgridDomain — that registers a verified sending domain as a first-class Pulumi resource and surfaces the DKIM CNAME records as stack outputs.
import * as pulumi from "@pulumi/pulumi"; class MailgridDomainProvider implements pulumi.dynamic.ResourceProvider { async create(inputs: { domain: string; apiKey: string }) { const res = await fetch( "https://api.mailgrid.space/api/domains/verify", { method: "POST", headers: { "Authorization": `Bearer ${inputs.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ domain: inputs.domain }), }, ); const data = await res.json(); return { id: inputs.domain, outs: { domain: inputs.domain, dkimTokens: data.data?.dkimTokens ?? [], status: data.data?.status ?? "pending", }, }; } } export class MailgridDomain extends pulumi.dynamic.Resource { public readonly dkimTokens!: pulumi.Output<string[]>; public readonly status!: pulumi.Output<string>; constructor(name: string, args: { domain: pulumi.Input<string>; apiKey: pulumi.Input<string> }, opts?: pulumi.CustomResourceOptions) { super(new MailgridDomainProvider(), name, { ...args, dkimTokens: undefined, status: undefined }, opts); } } // Usage in your Pulumi stack const domain = new MailgridDomain("sending-domain", { domain: "yourapp.com", apiKey: new pulumi.Config().requireSecret("mailgridApiKey"), }); export const dkimTokens = domain.dkimTokens; export const verifyStatus = domain.status;
Run pulumi stack output dkimTokens after apply to get the three CNAME values to add to DNS.
Kubernetes / Helm
Located at integrations/kubernetes/. A Helm chart named mailgrid-notify that installs a post-install and post-upgrade Job hook. The Job runs a one-shot container that calls the InboxOS API to confirm each deployment.
Install or upgrade
helm upgrade --install mailgrid-notify \
integrations/kubernetes/chart \
--namespace yourapp \
--set mailgrid.apiKey=$MAILGRID_API_KEY \
--set mailgrid.from=deploys@yourapp.com \
--set mailgrid.to=team@yourapp.com \
--set mailgrid.subject="Helm release {{ .Release.Name }} deployed to {{ .Release.Namespace }}"
Hook Job template
apiVersion: batch/v1 kind: Job metadata: name: "{{ .Release.Name }}-notify" annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "0" "helm.sh/hook-delete-policy": hook-succeeded spec: template: spec: restartPolicy: Never containers: - name: notify image: curlimages/curl:latest command: - sh - -c - | curl -s -X POST https://api.mailgrid.space/api/emails \ -H "Authorization: Bearer $MAILGRID_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"from\":\"$(MAILGRID_FROM)\",\"to\":\"$(MAILGRID_TO)\", \"subject\":\"{{ .Values.mailgrid.subject }}\", \"text\":\"Release {{ .Release.Name }} — revision {{ .Release.Revision }}\"}" env: - name: MAILGRID_API_KEY valueFrom: secretKeyRef: name: mailgrid-secret key: api-key - name: MAILGRID_FROM value: "{{ .Values.mailgrid.from }}" - name: MAILGRID_TO value: "{{ .Values.mailgrid.to }}"
Create the secret before installing the chart: kubectl create secret generic mailgrid-secret --from-literal=api-key=$MAILGRID_API_KEY -n yourapp
AWS CDK
Located at integrations/aws-cdk/. An L3 construct called MailgridDeploy that wires CodeDeploy lifecycle hooks to a Lambda function which calls the InboxOS API on deployment success or failure.
import * as cdk from "aws-cdk-lib"; import * as lambda from "aws-cdk-lib/aws-lambda"; import * as ssm from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; export class MailgridDeploy extends Construct { constructor(scope: Construct, id: string, props: { from: string; to: string; appName: string; apiKeyPath: string; // SSM SecureString parameter path }) { super(scope, id); const apiKey = ssm.StringParameter.valueForSecureStringParameter( this, "ApiKey", props.apiKeyPath, ); const notifyFn = new lambda.Function(this, "NotifyFn", { runtime: lambda.Runtime.NODEJS_20_X, handler: "index.handler", code: lambda.Code.fromAsset("integrations/aws-cdk/lambda"), environment: { MAILGRID_API_KEY: apiKey, FROM_EMAIL: props.from, TO_EMAIL: props.to, APP_NAME: props.appName, }, }); } } // Stack usage const app = new cdk.App(); const stack = new cdk.Stack(app, "MyAppStack"); new MailgridDeploy(stack, "DeployNotify", { from: "deploys@yourapp.com", to: "team@yourapp.com", appName: "MyApp", apiKeyPath: "/prod/mailgrid/api-key", });
Store the Mailgrid API key in AWS SSM Parameter Store as a SecureString before running cdk deploy:
aws ssm put-parameter \ --name /prod/mailgrid/api-key \ --type SecureString \ --value $MAILGRID_API_KEY cdk deploy MyAppStack
Docker
Located at integrations/docker/. An SMTP sidecar pattern for applications that only speak SMTP and cannot make HTTP API calls directly. A lightweight relay container accepts SMTP on port 587 and forwards it to InboxOS.
version: "3.9" services: app: image: yourapp:latest environment: SMTP_HOST: mailgrid-relay SMTP_PORT: "587" SMTP_USER: apikey SMTP_PASS: "${MAILGRID_API_KEY}" depends_on: - mailgrid-relay mailgrid-relay: image: mailgrid/smtp-relay:latest environment: MAILGRID_API_KEY: "${MAILGRID_API_KEY}" RELAY_LISTEN_PORT: "587" ALLOWED_NETWORKS: "172.16.0.0/12" ports: - "127.0.0.1:587:587" # bind to loopback only restart: unless-stopped # Optional: health-check container that sends a test email on startup smoke-test: image: curlimages/curl:latest depends_on: - mailgrid-relay entrypoint: - sh - -c - | sleep 3 curl -s -X POST https://api.mailgrid.space/api/emails \ -H "Authorization: Bearer $MAILGRID_API_KEY" \ -H "Content-Type: application/json" \ -d '{"from":"deploys@yourapp.com","to":"team@yourapp.com", "subject":"Container stack started","text":"Compose stack is up."}' environment: MAILGRID_API_KEY: "${MAILGRID_API_KEY}" restart: "no"
The SMTP relay accepts unauthenticated connections from ALLOWED_NETWORKS. Bind the port to 127.0.0.1 or a private Docker network — never to all interfaces on a public host.
Quick reference
| Tool | Integration path | Trigger |
|---|---|---|
| GitHub Actions | integrations/github-action/ |
on: push, on: release |
| GitLab CI | integrations/gitlab-ci/ |
Pipeline stage (when: on_success/on_failure) |
| Netlify | integrations/netlify/ |
Build plugin onSuccess / onError |
| Vercel | integrations/vercel/ |
Marketplace integration, env var injection |
| Cloudflare Pages | integrations/cloudflare-pages/ |
Pages Function proxy |
| Terraform | integrations/terraform/ |
terraform apply local-exec provisioner |
| Pulumi | integrations/pulumi/ |
Dynamic resource on pulumi up |
| Kubernetes / Helm | integrations/kubernetes/ |
Post-install / post-upgrade Job hook |
| AWS CDK | integrations/aws-cdk/ |
L3 construct, CodeDeploy Lambda hook |
| Docker | integrations/docker/ |
SMTP sidecar via Compose |