Webhooks API
The Webhooks API provides endpoints for creating issues and deployments programmatically. This is useful for integrating GuideMode with custom tools, CI/CD pipelines, or systems that don’t have native integrations.
Overview
Section titled “Overview”The v1 webhook API uses GuideMode’s internal data model directly, so no label mapping is required. All data created via these webhooks is tagged with provider "manual" to distinguish it from GitHub, Jira, or other native integrations.
Base URL
Section titled “Base URL”https://app.guidemode.dev/api/webhooks/v1Authentication
Section titled “Authentication”All requests require an API key in the Authorization header:
curl -H "Authorization: Bearer gai_your_api_key" \ -H "Content-Type: application/json" \ https://app.guidemode.dev/api/webhooks/v1/issuesYou can obtain an API key by:
- Running
guidemode loginin the CLI - Creating one in the GuideMode web UI under Settings > API Keys
Endpoints
Section titled “Endpoints”Create/Update Issue
Section titled “Create/Update Issue”POST /api/webhooks/v1/issuesCreates a new issue or updates an existing one (matched by externalId).
Request Body
Section titled “Request Body”{ "projectKey": "owner/repo", "externalId": "JIRA-123", "title": "Fix login button", "type": "bug", "state": "open", "body": "The login button doesn't work on Safari", "url": "https://jira.example.com/browse/JIRA-123", "authorUsername": "developer1", "assigneeUsername": "developer2", "labels": ["critical", "ui"], "createdAt": "2024-01-15T10:00:00Z", "closedAt": null, "metadata": { "priority": "high", "sprint": "Sprint 23" }}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
projectKey | string | Yes | Project identifier (e.g., "owner/repo") |
externalId | string | Yes | Unique ID in your source system |
title | string | Yes | Issue title |
type | string | Yes | One of: feature, bug, chore, discovery, incident, other |
state | string | Yes | One of: open, closed, in_progress |
number | number | No | Issue number (auto-generated if omitted) |
body | string | No | Issue description |
url | string | No | Link to source issue |
authorUsername | string | No | Creator username |
assigneeUsername | string | No | Primary assignee |
labels | string[] | No | Label names |
createdAt | string | No | ISO8601 timestamp (defaults to now) |
closedAt | string | No | ISO8601 timestamp when closed |
metadata | object | No | Custom key-value data |
Response
Section titled “Response”{ "success": true, "action": "created", "id": "550e8400-e29b-41d4-a716-446655440000", "externalId": "JIRA-123"}The action field indicates whether the issue was created or updated.
Create/Update Deployment
Section titled “Create/Update Deployment”POST /api/webhooks/v1/deploymentsCreates a new deployment or updates an existing one (matched by externalId).
Request Body
Section titled “Request Body”{ "projectKey": "owner/repo", "externalId": "deploy-123", "ref": "main", "sha": "abc123def456789012345678901234567890abcd", "environment": "production", "status": "success", "task": "deploy", "description": "Deploying v1.2.3", "url": "https://myapp.example.com", "creatorUsername": "deployer", "isProduction": true, "isRollback": false, "rollbackFromSha": null, "createdAt": "2024-01-15T10:00:00Z", "metadata": { "version": "1.2.3", "buildNumber": "456" }}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
projectKey | string | Yes | Project identifier (e.g., "owner/repo") |
externalId | string | Yes | Unique deployment ID |
ref | string | Yes | Branch/tag being deployed |
sha | string | Yes | Commit SHA (7-40 characters) |
environment | string | Yes | One of: production, staging, development, qa, preview, other |
status | string | Yes | One of: pending, queued, in_progress, success, failure, error, inactive |
task | string | No | Task type (default: "deploy") |
description | string | No | Deployment description |
url | string | No | Deployment URL |
creatorUsername | string | No | Deployer username |
isProduction | boolean | No | Override production detection |
isRollback | boolean | No | Flag as rollback (sets task to "rollback") |
rollbackFromSha | string | No | SHA being rolled back from (7-40 characters) |
createdAt | string | No | ISO8601 timestamp (defaults to now) |
metadata | object | No | Custom key-value data |
Response
Section titled “Response”{ "success": true, "action": "created", "id": "550e8400-e29b-41d4-a716-446655440001", "externalId": "deploy-123"}Error Handling
Section titled “Error Handling”Validation Errors (400)
Section titled “Validation Errors (400)”{ "error": "Validation failed", "details": [ { "path": "sha", "message": "sha must be 40 characters" }, { "path": "type", "message": "Invalid enum value" } ]}Authentication Errors (401)
Section titled “Authentication Errors (401)”{ "error": "Unauthorized"}Server Errors (500)
Section titled “Server Errors (500)”{ "error": "Internal server error"}Examples
Section titled “Examples”Create an issue:
curl -X POST https://app.guidemode.dev/api/webhooks/v1/issues \ -H "Authorization: Bearer gai_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "projectKey": "myorg/myrepo", "externalId": "TICKET-123", "title": "Implement dark mode", "type": "feature", "state": "open", "labels": ["enhancement", "ui"] }'Record a deployment:
curl -X POST https://app.guidemode.dev/api/webhooks/v1/deployments \ -H "Authorization: Bearer gai_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "projectKey": "myorg/myrepo", "externalId": "deploy-456", "ref": "main", "sha": "abc123def456789012345678901234567890abcd", "environment": "production", "status": "success" }'JavaScript/TypeScript
Section titled “JavaScript/TypeScript”const API_KEY = process.env.GUIDEMODE_API_KEY;const BASE_URL = 'https://app.guidemode.dev/api/webhooks/v1';
async function createIssue(issue: { projectKey: string; externalId: string; title: string; type: 'feature' | 'bug' | 'chore' | 'discovery' | 'incident' | 'other'; state: 'open' | 'closed' | 'in_progress'; body?: string; labels?: string[];}) { const response = await fetch(`${BASE_URL}/issues`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify(issue), });
if (!response.ok) { const error = await response.json(); throw new Error(`API error: ${error.error}`); }
return response.json();}
async function recordDeployment(deployment: { projectKey: string; externalId: string; ref: string; sha: string; environment: 'production' | 'staging' | 'development' | 'qa' | 'preview' | 'other'; status: 'pending' | 'queued' | 'in_progress' | 'success' | 'failure' | 'error';}) { const response = await fetch(`${BASE_URL}/deployments`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify(deployment), });
if (!response.ok) { const error = await response.json(); throw new Error(`API error: ${error.error}`); }
return response.json();}Python
Section titled “Python”import osimport requests
API_KEY = os.environ['GUIDEMODE_API_KEY']BASE_URL = 'https://app.guidemode.dev/api/webhooks/v1'
def create_issue(project_key, external_id, title, issue_type, state, **kwargs): response = requests.post( f'{BASE_URL}/issues', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json', }, json={ 'projectKey': project_key, 'externalId': external_id, 'title': title, 'type': issue_type, 'state': state, **kwargs, } ) response.raise_for_status() return response.json()
def record_deployment(project_key, external_id, ref, sha, environment, status, **kwargs): response = requests.post( f'{BASE_URL}/deployments', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json', }, json={ 'projectKey': project_key, 'externalId': external_id, 'ref': ref, 'sha': sha, 'environment': environment, 'status': status, **kwargs, } ) response.raise_for_status() return response.json()Project Auto-Creation
Section titled “Project Auto-Creation”When you create an issue or deployment, if the specified projectKey doesn’t exist, GuideMode will automatically create a new project with:
provider:"manual"sourceType:"manual"syncIssues:truesyncPullRequests:truesyncDeployments:true
This means you can start tracking work items immediately without pre-configuring projects.
Upsert Behavior
Section titled “Upsert Behavior”Both endpoints use upsert logic based on externalId:
- If an issue/deployment with the same
tenantId + provider + externalIdexists, it’s updated - If no match is found, a new record is created
This makes it safe to call the API multiple times with the same externalId - you’ll always get the latest state synced.
Rate Limits
Section titled “Rate Limits”- 100 requests per minute per API key
- 1000 requests per hour per API key
If you exceed these limits, you’ll receive a 429 Too Many Requests response.
Best Practices
Section titled “Best Practices”-
Use consistent external IDs: Use your source system’s unique identifiers as
externalIdto enable proper upsert behavior. -
Include timestamps: Provide
createdAtandclosedAtfor accurate metrics. Without these, GuideMode uses the current time. -
Set appropriate types: Use the correct
typefor issues to enable proper categorization in analytics dashboards. -
Track deployment status changes: Call the deployment endpoint multiple times as status changes (pending → in_progress → success/failure).
-
Use metadata for custom fields: Store additional data in the
metadatafield for reference, but note that it’s not indexed for queries.