Triggers automate work when repository events occur, such as pushes to a branch.
Create A Webhook Trigger
import { freestyle } from "freestyle";
const { repo } = await freestyle.git.repos.create();
const { triggerId } = await repo.triggers.create({
trigger: {
event: "push",
branches: ["main"],
globs: ["*.js"],
},
action: {
action: "webhook",
endpoint: "https://your-webhook-url.com",
},
});
console.log(triggerId);
branches and globs are optional filters.
Payload
Webhook triggers send a payload like this:
interface GitTriggerPayload {
event: "branchUpdate";
repoId: string;
branch: string;
commit: string;
}
Local Development
Use a tunnel such as Tailscale Funnel to expose a local development server.
tailscale funnel 3000
Use the generated public URL as the trigger endpoint while developing.
Webhook Signing
Webhooks include a JWT signature in the x-freestyle-signature header. The JWT contains a body_sha256 claim for the raw request body.
import crypto from "node:crypto";
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://git.freestyle.sh/.well-known/jwks.json"),
);
async function verifyWebhook(request: Request) {
const signature = request.headers.get("x-freestyle-signature");
if (!signature) throw new Error("Missing signature");
const bodyText = await request.text();
const { payload } = await jwtVerify(signature, JWKS, {
algorithms: ["EdDSA"],
});
const bodyHash = crypto.createHash("sha256").update(bodyText).digest("hex");
if (payload.body_sha256 !== bodyHash) {
throw new Error("Payload verification failed");
}
return JSON.parse(bodyText);
}
List Triggers
const { triggers } = await repo.triggers.list();
for (const trigger of triggers) {
console.log(trigger.id, trigger.trigger.event);
}
Delete A Trigger
await repo.triggers.delete({
triggerId: "trigger-id",
});