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.
.orchestrator/ in your GitHub repository.orchestrator/projects/<projectKey>/tickets/<ticketKey>/plan.md and tasks.md.orchestrator/ directory structure.orchestrator/config.jsonAdd 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" } } }
targets: Array of Replit webhook targets
name: Human-readable identifier (e.g., "main-repl", "staging-repl")webhookUrl: Full URL to your Replit webhook endpointsecret: (Optional) Shared secret for HMAC signature verificationenabled: (Optional, default: true) Whether this target is activedefaultTargetName: (Optional) Name of the target to use when no explicit target is specified
_packages/examples/replit-webhook-listener/ into your ReplIn the Replit shell:
bashnpm install express
Or add to package.json:
json{ "dependencies": { "express": "^4.18.0" } }
In Replit, go to Secrets (lock icon in sidebar) and add:
JMATE_WEBHOOK_SECRET (optional): Shared secret for webhook signature verification
secret in your .orchestrator/config.jsonGITHUB_REPO_URL (optional): Your GitHub repository URL
https://github.com/username/repo.githttps://your-repl-name.your-username.repl.co)https://your-repl-name.your-username.repl.co/ticket-mate-webhook.orchestrator/config.jsonAdd the webhook URL to your config (see configuration section above).
From your repository root, run:
bashtm replit-test-webhook
Or with a specific target:
bashtm replit-test-webhook --target main-repl
Or with a specific project:
bashtm replit-test-webhook --project CART
This will:
.orchestrator/config.jsonCheck your Replit logs to confirm the webhook was received.
When you run tm sync-tickets, webhooks are automatically sent if:
--notify-replit is true (default)To skip webhook notification:
bashtm sync-tickets --no-notify-replit
To use a specific target:
bashtm sync-tickets --replit-target staging-repl
.orchestrator Inside ReplitAfter pulling the latest code from GitHub, Replit can read ticket instructions:
javascriptconst 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); }
git pull origin main.orchestrator/projects/<projectKey>/tickets/ directoryplan.md and tasks.mdnpm testprogress.log.mdThe 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" }
X-JMATE-SOURCE: Always "ticket-mate"X-JMATE-EVENT: Event type (currently "tickets_synced")X-JMATE-SIGNATURE: HMAC-SHA256 signature (if secret is configured)If a secret is configured, verify the signature:
javascriptconst 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) ); }
secret in production: Configure JMATE_WEBHOOK_SECRET in Replit SecretsX-JMATE-SIGNATURE header if secret is setsecret values out of Git repositories.orchestrator/config.json matches your Repl's public URLtm replit-test-webhookJMATE_WEBHOOK_SECRET in Replit Secrets matches secret in .orchestrator/config.jsonIf you see "Replit target not found":
.orchestrator/config.jsonenabled is not falseHere's a complete example that pulls code, reads tickets, and runs tests:
javascriptconst 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 }); });
.orchestrator files.orchestratortm sync-ticketstm replit-test-webhookFor more information: