How to Receive GitHub Deployment Webhooks and Log Releases to MySQL
Verify an inbound GitHub deployment webhook with a GitHub-scheme signing connection, reshape the payload with a Transform node, insert a release record into a MySQL audit table, post a release summary to Slack, and return 202 to GitHub with a Response node.
What This Integration Does
When your team ships code, GitHub can send a deployment or deployment_status webhook every time an environment is updated. This Spojit workflow captures those events, writes a permanent row into a MySQL audit table, and announces each release in Slack. The result is a tamper-evident deployment log you can query later (who shipped what, to which environment, and when) plus an instant heads-up for the people who watch production.
The run model is webhook-driven. GitHub POSTs to your workflow's URL; Spojit verifies the request signature against a GitHub-scheme signing connection before any step runs, so unsigned or tampered payloads never execute. The parsed JSON body is available as {{ input }}. A Transform node flattens the relevant fields, a Connector node on the mysql connector inserts one row with insert-rows, a Slack message goes out, and a Response node returns 202 Accepted to GitHub. Each delivery produces exactly one audit row; because GitHub may retry a delivery, you can deduplicate on the event-id header so replays do not double-log.
Prerequisites
- A MySQL connection in Spojit (host, port, database, user with
INSERTpermission on your audit table). - An audit table to write to. For example:
CREATE TABLE deployment_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, delivery_id VARCHAR(64), repository VARCHAR(255), environment VARCHAR(64), ref VARCHAR(255), sha VARCHAR(64), state VARCHAR(32), actor VARCHAR(128), deployed_at DATETIME ); - A Slack connection and the target channel ID (for example
C0123ABCD). - A signing secret you control. You will paste the same secret into the GitHub webhook config and into the Spojit signing connection.
- Repository admin access on GitHub so you can add a webhook under Settings → Webhooks.
Step 1: Add a Webhook trigger with a GitHub-scheme signing connection
In a new workflow, add a Trigger node and set Trigger Type to Webhook. Under the signature settings, choose the GitHub scheme and select (or create) a signing connection that holds your secret. GitHub signs each delivery with HMAC SHA-256 and sends it in the X-Hub-Signature-256 header; the GitHub scheme verifies this for you, so only authentic deliveries run the workflow. Copy the workflow's webhook URL. In GitHub, open the repository's Settings → Webhooks → Add webhook, paste the URL as the Payload URL, set Content type to application/json, enter the same Secret, and under Let me select individual events tick Deployment statuses. The parsed body arrives as {{ input }}.
To guard against GitHub redelivering the same event, turn on dedup and point it at the X-GitHub-Delivery header so each delivery is processed once.
Step 2: Reshape the payload with a Transform node
Add a Transform node after the trigger to pull the handful of fields you care about out of GitHub's large payload and rename them to match your table columns. A deployment_status event nests the environment and state under deployment_status and the repo/ref under deployment and repository. Map the output to a flat object, for example:
{
"delivery_id": "{{ input.deployment_status.id }}",
"repository": "{{ input.repository.full_name }}",
"environment": "{{ input.deployment_status.environment }}",
"ref": "{{ input.deployment.ref }}",
"sha": "{{ input.deployment.sha }}",
"state": "{{ input.deployment_status.state }}",
"actor": "{{ input.sender.login }}",
"deployed_at": "{{ input.deployment_status.updated_at }}"
}
Save the result to a variable such as release. Downstream steps then reference {{ release.environment }}, {{ release.state }}, and so on. If you prefer field-by-field plucking instead of a single template, you can use a Connector node on the json connector with pick to keep only the keys you need, then set to rename them; the Transform node above does both in one step.
Step 3: Insert the release row into MySQL (Direct mode)
Add a Connector node on the mysql connector in Direct mode and pick the insert-rows tool. Direct mode is deterministic and costs no AI credits, which is what you want for a predictable single-table write. Set table to deployment_log and set rows to an array containing your reshaped object:
[
{
"delivery_id": "{{ release.delivery_id }}",
"repository": "{{ release.repository }}",
"environment": "{{ release.environment }}",
"ref": "{{ release.ref }}",
"sha": "{{ release.sha }}",
"state": "{{ release.state }}",
"actor": "{{ release.actor }}",
"deployed_at": "{{ release.deployed_at }}"
}
]
The object keys must match the table column names. If your connection's default database is not the one holding the table, set the optional database field. Save the tool output to a variable like insertResult so you can confirm the write in the execution log.
Step 4: Post a release summary to Slack
Add another Connector node, this time on the slack connector in Direct mode, and choose the send-message tool. Set channel to your channel ID (for example C0123ABCD) and compose text from your reshaped variables:
:rocket: *{{ release.repository }}* deployed to *{{ release.environment }}*
State: {{ release.state }}
Ref: {{ release.ref }} ({{ release.sha }})
By: {{ release.actor }} at {{ release.deployed_at }}
If you would rather only ping the channel on successful deploys, place a Condition node before this step that checks whether {{ release.state }} equals success, and connect only the true branch to the Slack node. As an alternative to Slack, a Send Email node can mail the same summary using Spojit's built-in mail service with no connection required.
Step 5: Return 202 to GitHub with a Response node
Add a Response node as the final step so the synchronous webhook caller gets a clean acknowledgement. Set the status to 202 and return a small body such as:
{ "logged": true, "delivery_id": "{{ release.delivery_id }}" }
GitHub treats any 2xx response as a successful delivery and will not retry. Returning promptly from the Response node keeps your webhook deliveries green in GitHub's Recent Deliveries tab. Without a Response node, the webhook still resolves with Spojit's default 202 and an executionId, but an explicit Response node lets you confirm exactly what was logged.
Tips
- Keep the MySQL write in Direct mode. A single-table insert never needs an AI agent, and Direct mode keeps every run cheap and predictable.
- Store both
delivery_idandsha. The delivery id is unique per event, while the SHA lets you correlate the audit row with the exact commit that shipped. - Normalize timestamps. GitHub sends ISO 8601 strings like
2026-06-21T14:03:00Z; if yourDATETIMEcolumn rejects the trailingZ, add a Connector node on the date connector withformatto convert it toYYYY-MM-DD HH:mm:ssbefore the insert. - Use Miraxa, the intelligent layer across your automation, to scaffold the canvas: try "Add a Webhook trigger with a GitHub signing connection, a Transform node, a MySQL insert-rows Connector node, a Slack send-message node, and a Response node returning 202."
Common Pitfalls
- Wrong signing scheme. If you leave the scheme on Spojit or Custom, GitHub's
X-Hub-Signature-256header will not validate and every delivery is rejected. Pick the GitHub scheme. - Mismatched secrets. The secret in the GitHub webhook config must exactly match the secret in the Spojit signing connection, with no leading or trailing whitespace.
- Delivery retries. GitHub retries failed deliveries, so without dedup on
X-GitHub-Deliverya transient error can produce duplicate audit rows. Turn dedup on. - Column drift. The keys in your
rowsobject must match the table columns. A renamed or missing column makesinsert-rowsfail; re-check the table with thedescribe-tabletool when in doubt. - Event type confusion. A
deploymentevent and adeployment_statusevent have different shapes. The templates here assume Deployment statuses; if you subscribe to plain deployments, adjust the Transform paths.
Testing
Before relying on live deploys, validate end to end on a small scope. In GitHub's webhook page, open Recent Deliveries and use Redeliver on a sample deployment_status event (or trigger a deploy in a throwaway environment). Watch the run in Spojit's execution log: confirm the trigger accepted the signature, the Transform produced the flat release object, insert-rows returned a success result, the Slack message landed, and the Response node returned 202. Query SELECT * FROM deployment_log ORDER BY id DESC LIMIT 5; to verify the row, then redeliver the same event once more to confirm dedup prevents a duplicate before you point production repositories at the workflow.