LogoFreestyle

Triggers and Webhooks

Triggers let you automate actions when events occur in your git repositories, such as pushes to specific branches.

Creating a Trigger

Create a webhook trigger that fires on push events.

import { freestyle } from "freestyle";

const { repo } = await freestyle.git.repos.create();

const { triggerId } = await repo.triggers.create({
  trigger: {
    event: "push",
    branches: ["main"], // optional: filter by branch
    globs: ["*.js"], // optional: filter by file patterns
  },
  action: {
    action: "webhook",
    endpoint: "https://your-webhook-url.com",
  },
});

console.log(`Trigger created: ${triggerId}`);

When your trigger is activated, a payload is sent to the specified endpoint in the following format.

interface GitTriggerPayload {
  event: "branchUpdate";
  repoId: string;
  branch: string; // The branch that was updated
  commit: string; // The SHA of the commit
}

Local Development

For local development, use a tool like Tailscale to create a secure tunnel to your localhost.

Install Tailscale following the quickstart guide, then expose your local server:

# Replace 3000 with your server's port
tailscale funnel 3000

The output provides a public URL you can use as the webhook URL in your trigger configuration.

Webhook Signing

Webhooks include a signature in the x-freestyle-signature header for verifying request authenticity. The signature is a JWT (EdDSA) containing a body_sha256 field with the SHA256 hash of the webhook payload.

The public key for verification is available at https://git.freestyle.sh/.well-known/jwks.json.

import crypto from 'crypto';
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://git.freestyle.sh/.well-known/jwks.json')
);

async function verifyWebhook(request) {
  const signature = request.headers['x-freestyle-signature'];
  const bodyText = await request.text(); // Get raw body text

  // Verify JWT signature
  const { payload } = await jwtVerify(signature, JWKS, {
    algorithms: ['EdDSA'],
  });

  // Verify payload hash
  const payloadHash = crypto
    .createHash('sha256')
    .update(bodyText)
    .digest('hex');

  if (payload.body_sha256 !== payloadHash) {
    throw new Error('Payload verification failed');
  }

  // Parse body after verification
  const body = JSON.parse(bodyText);
  return { payload, body };
}

Listing Triggers

List all triggers for a repository.

const { triggers } = await repo.triggers.list();

for (const trigger of triggers) {
  console.log(trigger.id, trigger.trigger.event, trigger.action.endpoint);
}

Response structure:

interface TriggerListResponse {
  triggers: {
    id: string;
    repositoryId: string;
    trigger: {
      event: "push";
      branches?: string[] | null;
      globs?: string[] | null;
    };
    action: {
      action: "webhook";
      endpoint: string;
    };
    managed: boolean;
    createdAt: string;
  }[];
}

Deleting Triggers

Remove a trigger by its ID.

await repo.triggers.delete({ triggerId: "trigger-id" });

On this page

Freestyle AI

Documentation assistant

Experimental: AI responses may not always be accurate—please verify important details with the official documentation.

How can I help?

Ask me about Freestyle while you browse the docs.