-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
guyinwonder168 edited this page Feb 7, 2026
·
2 revisions
This guide covers the complete webhook payload structure and response codes.
- Event Payload Structure
- Issue Payload Examples
- Time Entry Payload Examples
- Payload Comparison
- Changes Payload
- Response Status Codes
- Webhook Signature
All webhook payloads follow this base structure:
{
"event_id": "string (UUID)",
"event_type": "issue|time_entry",
"action": "created|updated|deleted",
"occurred_at": "ISO 8601 datetime",
"redmine_url": "string (URL)",
"actor": {
"id": "integer",
"login": "string",
"name": "string",
"email": "string (optional)"
},
"resource": {
// Issue or TimeEntry object based on event_type
}
}| Field | Type | Description | Example |
|---|---|---|---|
| event_id | UUID string | Unique identifier for this event | |
| event_type | string | Type of resource: "issue" or "time_entry" | |
| action | string | What happened: "created", "updated", or "deleted" | |
| occurred_at | ISO 8601 datetime | When the event happened in Redmine | |
| redmine_url | string | Direct link to the Redmine resource | |
| actor | object | User who triggered the event | |
| resource | object | The actual issue or time entry data |
{
"event_id": "550e8400-e29b-41d4-a716-446614014",
"event_type": "issue",
"action": "created",
"occurred_at": "2026-02-03T12:00:00Z",
"redmine_url": "https://redmine.example.com/issues/123",
"actor": {
"id": 1,
"login": "admin",
"name": "Administrator"
},
"issue": {
"id": 123,
"subject": "Bug fix needed",
"description": "Fix authentication in login flow",
"status": { "id": 1, "name": "New" },
"priority": { "id": 4, "name": "High" },
"tracker": { "id": 1, "name": "Bug" },
"project": { "id": 5, "name": "Main Project" },
"author": { "id": 1, "login": "admin", "name": "Administrator" },
"is_private": false,
"created_on": "2026-02-03T12:00:00Z",
"updated_on": "2026-02-03T12:00:00Z"
}
}{
"event_id": "550e8400-e29b-41d4-a716-446614015",
"event_type": "issue",
"action": "updated",
"occurred_at": "2026-02-03T12:00:00Z",
"redmine_url": "https://redmine.example.com/issues/123",
"actor": {
"id": 2,
"login": "developer",
"name": "Developer",
"email": "developer@example.com"
},
"issue": {
"id": 123,
"subject": "Bug fix needed",
"description": "Fix authentication in login flow",
"status": { "id": 2, "name": "In Progress" },
"priority": { "id": 4, "name": "High" },
"tracker": { "id": 1, "name": "Bug" },
"assigned_to": { "id": 2, "login": "developer", "name": "Developer" },
"project": { "id": 5, "name": "Main Project" },
"author": { "id": 1, "login": "admin", "name": "Administrator" },
"is_private": false,
"created_on": "2026-02-02T10:00:00Z",
"updated_on": "2026-02-03T12:00:00Z",
"last_note": "Looking into this",
"custom_fields": [
{ "id": 1, "name": "Severity", "value": "Critical" }
]
},
"changes": {
"status": { "old": { "id": 1, "name": "New" }, "new": { "id": 2, "name": "In Progress" } },
"assigned_to": { "old": null, "new": { "id": 2, "login": "developer" } },
"priority": { "old": { "id": 5, "name": "Normal" }, "new": { "id": 4, "name": "High" }
}
}The changes object (included in Standard/Full mode) contains all modified fields:
"changes": {
"status": {
"old": { "id": 1, "name": "New" },
"new": { "id": 2, "name": "In Progress" }
},
"assigned_to": {
"old": null,
"new": { "id": 2, "login": "developer" }
},
"priority": {
"old": { "id": 5, "name": "Normal" },
"new": { "id": 4, "name": "High" }
},
"custom_fields": {
"1": { "old": "Low", "new": "Critical" }
},
"estimated_hours": {
"old": 5.0,
"new": 8.0
},
"done_ratio": {
"old": 0,
"new": 0
},
"is_private": {
"old": false,
"new": true
},
"category": {
"old": { "id": 1, "name": "Development" },
"new": { "id": 2, "name": "Support" }
}
}{
"event_id": "760e6600-f12c-34d5-b678-532610428",
"event_type": "time_entry",
"action": "created",
"occurred_at": "2026-02-03T12:00:00Z",
"redmine_url": "https://redmine.example.com/time_entries/45",
"actor": {
"id": 2,
"login": "developer",
"name": "Developer"
},
"time_entry": {
"id": 45,
"project": {
"id": 5,
"name": "Main Project"
},
"issue": {
"id": 123,
"subject": "Bug fix needed"
},
"user": {
"id": 2,
"login": "developer",
"name": "Developer"
},
"activity": {
"id": 9,
"name": "Development"
},
"hours": 2.5,
"comments": "Fixed authentication bug",
"spent_on": "2026-02-03",
"created_on": "2026-02-03T12:00:00Z",
"updated_on": "2026-02-03T12:00:00Z"
}
}{
"event_id": "760e6600-f12c-34d5-b678-532610429",
"event_type": "time_entry",
"action": "updated",
"occurred_at": "2026-02-03T12:00:00Z",
"redmine_url": "https://redmine.example.com/time_entries/45",
"actor": {
"id": 2,
"login": "developer",
"name": "Developer"
},
"time_entry": {
"id": 45,
"project": {
"id": 5,
"name": "Main Project"
},
"issue": {
"id": 123,
"subject": "Bug fix needed"
},
"user": {
"id": 2,
"login": "developer",
"name": "Developer"
},
"activity": {
"id": 9,
"name": "Development"
},
"hours": 3.0,
"comments": "Additional investigation required",
"spent_on": "2026-02-03",
"created_on": "2026-02-02T10:00:00Z",
"updated_on": "2026-02-03T12:00:00Z"
},
"changes": {
"hours": { "old": 2.5, "new": 3.0 },
"comments": { "old": "Fixed authentication bug", "new": "Additional investigation required" }
}
}| Feature | Minimal | Standard | Full |
|---|---|---|---|
| Size | Small (~1KB) | Medium (~5KB) | Large (~20KB) |
| Issue Fields | Core fields only | Core + last_note | All fields |
| Time Entry Fields | Core fields only | Core + comments | All fields |
| Changes Object | Not included | Included | Included |
| Journal/Notes | Not included | Last note only | Full history |
| Custom Fields | Not included | Included | Included |
| Use Case | Simple notifications | Most integrations | Data sync |
Minimal Mode:
- Slack notifications (simple format)
- Microsoft Teams (cards only)
- High-volume systems
- Testing webhook connectivity
- Bandwidth-constrained environments
Standard Mode:
- Most webhook integrations
- When you need issue context
- Balancing detail vs. size
- Audit trails and reporting
Full Mode:
- Custom integrations requiring all data
- Data synchronization systems
- Data warehousing and analytics
- When bandwidth is not a concern
| Code | Meaning | Plugin Action |
|---|---|---|
| 200 | OK | Delivery marked "Success" |
| 201 | Created | Resource created (applicable for some integrations) |
| 202 | Accepted | Request accepted (applicable for some integrations) |
| 204 | No Content | Acknowledged without data |
| Code | Meaning | Plugin Action | Common Causes |
|---|---|---|---|
| 400 | Bad Request | Invalid JSON, missing required fields | |
| 401 | Unauthorized | Invalid API key, wrong token | |
| 403 | Forbidden | IP not whitelisted, insufficient permissions | |
| 404 | Not Found | Invalid endpoint URL | |
| 429 | Too Many Requests | Rate limiting exceeded |
| Code | Meaning | Plugin Action | Common Causes |
|---|---|---|---|
| 500 | Internal Server Error | External service error | |
| 502 | Bad Gateway | Upstream server error | |
| 503 | Service Unavailable | External service down or overloaded | |
| 504 | Gateway Timeout | External service timeout |
When HMAC signature is enabled, the payload includes an additional field:
{
"event_id": "uuid",
"event_type": "issue",
"action": "created",
"occurred_at": "2026-02-03T12:00:00Z",
"redmine_url": "https://redmine.example.com/issues/123",
"actor": { "id": 1, "login": "admin" },
"resource": {
"id": 123,
"subject": "Bug fix needed"
},
"signature": "a1b2c3d4e5f67898be2dbd5f8f3a4f4c4e9b1c266"
}Python Example:
import hmac
import json
import hashlib
# Shared secret (from external service configuration)
SECRET = "your_secret_key_here"
def verify_webhook(payload_json):
# Extract signature and payload body
received_signature = payload_json.get('signature')
payload_body = json.dumps({k: v for k, v in payload_json.items() if k != 'signature'})
# Calculate expected signature
expected_signature = hmac.new(
payload_body.encode('utf-8'),
SECRET.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Verify
if received_signature == expected_signature:
print("✓ Signature valid")
return True
else:
print("✗ Invalid signature")
return False
# Usage
payload = {
"event_id": "550e8400-e29b-41d4-a716-446614014",
"event_type": "issue",
"action": "created",
"occurred_at": "2026-02-03T12:00:00Z",
"redmine_url": "https://redmine.example.com/issues/123",
"actor": { "id": 1, "login": "admin" },
"resource": { "id": 123, "subject": "Bug fix" }
}
if verify_webhook(payload):
print("Processing webhook...")
else:
print("Rejecting webhook...")Ruby Example:
require 'openssl'
require 'json'
SECRET = "your_secret_key_here"
def verify_webhook(payload)
received_sig = payload['signature']
# Remove signature from payload for calculation
payload_body = payload.except('signature').to_json
# Calculate HMAC
expected_sig = OpenSSL::HMAC.hex(
SECRET,
payload_body,
OpenSSL::Digest::SHA256
)
# Verify
expected_sig == received_sig
end
# Usage
require 'json'
payload = JSON.parse(request.body)
if verify_webhook(payload)
# Process webhook
else
status 401
body 'Invalid signature'
endTo prevent replay attacks, the plugin includes event timestamp:
{
"occurred_at": "2026-02-03T12:00:00Z",
"timestamp": 1706971200 // Unix timestamp (optional)
}Verification:
from datetime import datetime, timezone
payload_ts = payload['occurred_at'] # ISO 8601 string
server_time = datetime.now(timezone.utc).isoformat()
# Reject if event is too old (e.g., older than 5 minutes)
if (server_time - payload_ts).total_seconds() > 300:
print("Event too old - possible replay attack")
status 401When a webhook delivery fails, the response may include error details:
{
"status": 404,
"error": {
"code": "ENDPOINT_NOT_FOUND",
"message": "Webhook endpoint not found",
"details": "The requested URL does not exist"
},
"attempt": 3,
"next_retry_at": "2026-02-03T12:15:00Z"
}| Error Code | Description | Retry Strategy |
|---|---|---|
| VALIDATION_ERROR | Invalid JSON structure | No retry (fix endpoint config) |
| AUTHENTICATION_FAILED | Invalid credentials | No retry (fix auth first) |
| ENDPOINT_NOT_FOUND | URL incorrect | No retry (fix endpoint URL) |
| RATE_LIMIT_EXCEEDED | Too many requests | Retry with delay |
| TIMEOUT_ERROR | External service timeout | Retry immediately |
| SERVER_ERROR | 5xx status | Retry with backoff |
See detailed payload examples above or:
- Configuration - Payload mode setup
- Usage Guide - Monitoring deliveries
- Security - Authentication methods
| Problem | Solution | Documentation |
|---|---|---|
| Unexpected payload format | Check endpoint event configuration | Configuration |
| Missing fields | Verify payload mode setting | Payload Comparison |
| Authentication failures | Check credentials/IP whitelist | Security |
| 429 errors | Implement rate limiting | Security |
- Home - Documentation index
- Troubleshooting - Common issues and solutions
- Security - Complete security guide
- GitHub Issues - Report bugs
Last Updated: 2026-02-03
Plugin Version: 1.0.0-RC1