Go to Integrations

Replit Integration with Ticket Mate

Ticket Mate integrates with Replit through a file-based approach with optional webhook notifications. This integration does not require OAuth or login—it works purely through Git repositories and HTTP webhooks.

Conceptual Overview

How It Works

  1. Ticket Mate writes instruction files to .orchestrator/ in your GitHub repository
  2. Replit pulls the repository from GitHub (or you push changes)
  3. Replit reads .orchestrator/projects/<projectKey>/tickets/<ticketKey>/plan.md and tasks.md
  4. Optionally, Ticket Mate sends webhook notifications to a Replit-hosted endpoint when tickets are synced
  5. Replit (or Replit Automation/Agent) executes whatever scripts or tests you configure

Key Principles

  • No OAuth/Login: Ticket Mate never "logs in" to Replit
  • File-based: All instructions live in .orchestrator/ directory structure
  • Webhook notifications: Optional HTTP POST to notify Replit when tickets are synced
  • Git-centric: Both systems work from the same GitHub repository

Configuring .orchestrator/config.json

Add Replit targets to your .orchestrator/config.json:

json
{
  "jiraBaseUrl": "https://pamcms.atlassian.net",
  "jiraProjectKey": "CART",
  "defaultBranch": "main",
  "integrations": {
    "replit": {
      "targets": [
        {
          "name": "main-repl",
          "webhookUrl": "https://your-repl-name.your-username.repl.co/ticket-mate-webhook",
          "secret": "your-shared-secret-here",
          "enabled": true
        },
        {
          "name": "staging-repl",
          "webhookUrl": "https://staging-repl.your-username.repl.co/ticket-mate-webhook",
          "secret": "different-secret-for-staging",
          "enabled": true
        }
      ],
      "defaultTargetName": "main-repl"
    }
  }
}

Configuration Fields

  • targets: Array of Replit webhook targets

    • name: Human-readable identifier (e.g., "main-repl", "staging-repl")
    • webhookUrl: Full URL to your Replit webhook endpoint
    • secret: (Optional) Shared secret for HMAC signature verification
    • enabled: (Optional, default: true) Whether this target is active
  • defaultTargetName: (Optional) Name of the target to use when no explicit target is specified

Setting Up a Replit Webhook Listener

Step 1: Create a New Repl

  1. Go to Replit
  2. Click "Create Repl"
  3. Choose "Node.js" template
  4. Copy the example code from _packages/examples/replit-webhook-listener/ into your Repl

Step 2: Install Dependencies

In the Replit shell:

bash
npm install express

Or add to package.json:

json
{
  "dependencies": {
    "express": "^4.18.0"
  }
}

Step 3: Configure Secrets

In Replit, go to Secrets (lock icon in sidebar) and add:

  • JMATE_WEBHOOK_SECRET (optional): Shared secret for webhook signature verification

    • Must match the secret in your .orchestrator/config.json
    • If not set, webhook signature verification is skipped
  • GITHUB_REPO_URL (optional): Your GitHub repository URL

    • Example: https://github.com/username/repo.git
    • Used for pulling latest code

Step 4: Get Your Repl's Public URL

  1. Click the "Run" button in Replit
  2. Copy the public URL (e.g., https://your-repl-name.your-username.repl.co)
  3. The webhook endpoint will be: https://your-repl-name.your-username.repl.co/ticket-mate-webhook

Step 5: Update .orchestrator/config.json

Add the webhook URL to your config (see configuration section above).

Testing the Integration

Test Webhook from CLI

From your repository root, run:

bash
tm replit-test-webhook

Or with a specific target:

bash
tm replit-test-webhook --target main-repl

Or with a specific project:

bash
tm replit-test-webhook --project CART

This will:

  1. Load your .orchestrator/config.json
  2. Resolve the target (default or specified)
  3. Send a test payload with a dummy ticket
  4. Show the HTTP response

Check your Replit logs to confirm the webhook was received.

Test During Sync

When you run tm sync-tickets, webhooks are automatically sent if:

  • --notify-replit is true (default)
  • At least one ticket was synced
  • A valid target is configured

To skip webhook notification:

bash
tm sync-tickets --no-notify-replit

To use a specific target:

bash
tm sync-tickets --replit-target staging-repl

Using .orchestrator Inside Replit

Reading Ticket Instructions

After pulling the latest code from GitHub, Replit can read ticket instructions:

javascript
const fs = require("fs");
const path = require("path");

// Read plan for a specific ticket
const planPath = path.join(
  ".orchestrator",
  "projects",
  "CART",
  "tickets",
  "CART-123",
  "plan.md"
);
if (fs.existsSync(planPath)) {
  const plan = fs.readFileSync(planPath, "utf-8");
  console.log("Plan:", plan);
}

// Read tasks
const tasksPath = path.join(
  ".orchestrator",
  "projects",
  "CART",
  "tickets",
  "CART-123",
  "tasks.md"
);
if (fs.existsSync(tasksPath)) {
  const tasks = fs.readFileSync(tasksPath, "utf-8");
  console.log("Tasks:", tasks);
}

Example Workflow

  1. Webhook received → Replit listener logs the event
  2. Pull latest code: git pull origin main
  3. List tickets: Read .orchestrator/projects/<projectKey>/tickets/ directory
  4. Process tickets: For each ticket with status "Ready for Dev" or "In Progress":
    • Read plan.md and tasks.md
    • Execute tests: npm test
    • Run scripts based on ticket type
    • Update progress in progress.log.md
  5. Commit changes: Push updates back to GitHub

Webhook Payload Structure

The webhook sends a JSON payload:

json
{
  "event": "tickets_synced",
  "source": "ticket-mate",
  "repoName": "my-repo",
  "repoUrl": "https://github.com/username/repo",
  "defaultBranch": "main",
  "projectKey": "CART",
  "tickets": [
    {
      "key": "CART-123",
      "projectKey": "CART",
      "summary": "Implement search filter",
      "status": "In Progress",
      "url": "https://pamcms.atlassian.net/browse/CART-123",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "syncedAt": "2024-01-15T10:30:00Z"
}

Headers

  • X-JMATE-SOURCE: Always "ticket-mate"
  • X-JMATE-EVENT: Event type (currently "tickets_synced")
  • X-JMATE-SIGNATURE: HMAC-SHA256 signature (if secret is configured)

Signature Verification

If a secret is configured, verify the signature:

javascript
const crypto = require("crypto");

function verifySignature(body, signature, secret) {
  const payload = JSON.stringify(body);
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Security Best Practices

  1. Always use secret in production: Configure JMATE_WEBHOOK_SECRET in Replit Secrets
  2. Verify signatures: Always verify X-JMATE-SIGNATURE header if secret is set
  3. Never commit secrets: Keep secret values out of Git repositories
  4. Use HTTPS: Always use HTTPS URLs for webhook endpoints

Troubleshooting

Webhook Not Received

  1. Check Replit logs for errors
  2. Verify the webhook URL in .orchestrator/config.json matches your Repl's public URL
  3. Ensure your Repl is running (click "Run" button)
  4. Test with tm replit-test-webhook

Invalid Signature Error

  1. Verify JMATE_WEBHOOK_SECRET in Replit Secrets matches secret in .orchestrator/config.json
  2. Ensure both are set (or both are unset for testing)

Webhook Received But No Action

  1. Check the TODO comments in your webhook listener code
  2. Add your custom logic to process tickets
  3. Check Replit logs for any errors

Target Not Found

If you see "Replit target not found":

  1. Verify the target name in .orchestrator/config.json
  2. Check that enabled is not false
  3. List available targets: The error message will show all configured targets

Example: Complete Replit Automation

Here's a complete example that pulls code, reads tickets, and runs tests:

javascript
const express = require("express");
const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");

app.post("/ticket-mate-webhook", async (req, res) => {
  const payload = req.body;

  // Verify signature (if secret is set)
  // ... (see signature verification above)

  console.log(`📥 Received webhook: ${payload.tickets.length} tickets synced`);

  // Pull latest code
  exec("git pull origin main", (error, stdout, stderr) => {
    if (error) {
      console.error("Git pull failed:", error);
      return;
    }

    // Process each ticket
    payload.tickets.forEach((ticket) => {
      const ticketDir = path.join(
        ".orchestrator",
        "projects",
        ticket.projectKey,
        "tickets",
        ticket.key
      );

      // Read plan
      const planPath = path.join(ticketDir, "plan.md");
      if (fs.existsSync(planPath)) {
        const plan = fs.readFileSync(planPath, "utf-8");
        console.log(`📋 Plan for ${ticket.key}:`, plan.substring(0, 100));
      }

      // Run tests if ticket is in progress
      if (ticket.status === "In Progress") {
        exec("npm test", (error, stdout, stderr) => {
          if (error) {
            console.error(`Tests failed for ${ticket.key}:`, error);
          } else {
            console.log(`✅ Tests passed for ${ticket.key}`);
          }
        });
      }
    });
  });

  res.json({ success: true });
});

Next Steps

  • Integrate with Replit Database: Track processed tickets
  • Add GitHub Actions: Automate pulling and processing .orchestrator files
  • Set up notifications: Slack, Discord, or email alerts
  • Automate test runs: Run tests based on ticket status
  • Custom scripts: Execute project-specific automation

Related Documentation

Support

For more information: