Receive a push from the forge and trigger its build
The forge's push-to-deploy door. git.hanzo.ai runs as a separate server, so its pushes never reach this fleet's own receive-pack; without this a push to…
POST /v1/platform/hook
| Address | https://api.hanzo.ai/v1/platform/hook |
| Method | POST |
| Operation | post_platform_hook |
| Auth | Authorization: Bearer $HANZO_API_KEY |
The forge's push-to-deploy door. git.hanzo.ai runs as a separate server, so its pushes never reach this fleet's own receive-pack; without this a push to the host we call canonical builds nothing. A verified push is handed to the SAME two seams a native push travels — the single-registrant deploy trigger, and the many-subscriber lifecycle stream that notifies and indexes — and the build decision itself stays downstream in the one place that knows what a push means.
PUBLIC at the JWT layer, because the forge carries no Hanzo session: AUTHENTICATION IS THE SIGNATURE. The HMAC covers the raw bytes and is verified BEFORE the payload is parsed, so an unauthenticated body is never decoded. The secret is read from KMS; a deployment that cannot read it answers 503 and processes nothing, rather than trusting a delivery it could not check. The body is read UNCOMPRESSED — a request declaring a Content-Encoding is refused 415 before it is touched, because decoding one is unbounded work bought with a few bytes and no credential. A bad signature is 401, a payload over 8 MiB is 413, and a malformed one 400.
A verified push that reaches both seams answers 200 with fired true and the NUMBER OF BUILDS it launched — zero is ordinary, since most pushes track no application, and it is the answer 'fired' cannot give. A push that could not be dispatched answers 500: the delivery page shows it red, and the Replay that prompts reaches a fresh attempt rather than being declined as already landed.
The deliveries deliberately ignored answer 200 with a reason and nothing else: a payload that is not a push, a ref DELETE (a zero after has no commit to build), a BOT-authored push (release automation pushes as the forge's own Actions user, and a release must never rebuild itself), a push from a forge namespace that maps to no org, and a redelivery of a push already fired. Branches and tags both reach the build trigger, because releases are cut by tag and filtering here would silently stop publishing.
Request
11 fields, body application/json.
| Field | In | Type | Required | Description |
|---|---|---|---|---|
after | body | string | — | |
before | body | string | — | |
pusher | body | object | — | |
pusher.login | body | string | — | |
pusher.username | body | string | — | |
ref | body | string | — | |
repository | body | object | — | |
repository.name | body | string | — | |
repository.owner | body | object | — | |
repository.owner.login | body | string | — | |
repository.owner.username | body | string | — |
Response
| Status | Body | Meaning |
|---|---|---|
2XX | verdict | Success |
2XX body — 7 fields.
| Field | In | Type | Always | Description |
|---|---|---|---|---|
builds | body | integer | — | |
commit | body | string | — | |
fired | body | boolean | — | |
org | body | string | — | |
reason | body | string | — | |
ref | body | string | — | |
repo | body | string | — |
Failure carries the platform error shape — see Errors.
Examples
hanzo has no subcommand for this operation — the CLI serves only what cloud's live route table confirms. Use HTTP or an SDK.
import { Configuration, PlatformApi } from 'hanzoai';
const api = new PlatformApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postPlatformHook({ after: "<after>", before: "<before>" });from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import PlatformApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = PlatformApi(client).post_platform_hook(after="<after>", before="<before>")cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.PlatformAPI.PostPlatformHook(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, platform_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = platform_api::post_platform_hook(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.PlatformApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new PlatformApi(client).postPlatformHook();The method above is the one at the current release of the document. [email protected] (npm) and [email protected] (PyPI) were generated from an earlier release, where this operation carried a different id, so it spells the method differently — regenerating the clients is what makes the two agree. SDKs →
curl -X POST https://api.hanzo.ai/v1/platform/hook \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"after": "<after>",
"before": "<before>"
}'The door reaches platform through the platform tool, which names its 39 operations with its own verbs — this one among them, under a name only the door declares. describe explains any of them:
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "describe",
"arguments": {
"op": "list_builds"
}
}
}'How is this guide?