Freestyle Docs

Freestyle / Docs

Triggers

Send webhooks when Freestyle Git repositories receive pushes.

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",
});
esc