Discord Agent API
API reference for the self-hosted Discord Agent.
Overview
The Discord Agent API provides endpoints for automated Discord role delivery. Your self-hosted Discord Agent polls these endpoints to:
- Fetch pending role operations
- Claim operations (prevent duplicate processing)
- Confirm successful completion
- Report failures
Authentication
All Discord Agent endpoints require an API token with the discord:agent scope.
curl -X GET "https://yourstore.pixlpay.net/api/v1/discord-agent/pending" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"Create a token in your dashboard: Settings > API Tokens with the discord:agent scope selected.
Base URL
https://yourstore.pixlpay.net/api/v1/discord-agent/Replace yourstore with your store's subdomain.
Endpoints
Get Pending Operations
Fetch all pending role operations for your store.
GET /api/v1/discord-agent/pendingResponse
{
"success": true,
"data": [
{
"id": 1,
"operation": "assign",
"guild_id": "123456789012345678",
"discord_user_id": "987654321098765432",
"role_id": "111222333444555666",
"role_name": "VIP",
"order_id": 42,
"order_number": "ORD-2025-042",
"created_at": "2025-01-20T14:30:00Z"
},
{
"id": 2,
"operation": "remove",
"guild_id": "123456789012345678",
"discord_user_id": "555666777888999000",
"role_id": "111222333444555666",
"role_name": "VIP",
"subscription_id": 15,
"created_at": "2025-01-20T14:35:00Z"
}
]
}Operation Fields
| Field | Type | Description |
|---|---|---|
id | integer | Unique operation ID |
operation | string | Either assign or remove |
guild_id | string | Discord server (guild) ID |
discord_user_id | string | Discord user ID |
role_id | string | Discord role ID to assign/remove |
role_name | string | Human-readable role name |
order_id | integer | Associated order ID (if applicable) |
order_number | string | Order number (if applicable) |
subscription_id | integer | Associated subscription ID (if applicable) |
created_at | string | ISO 8601 timestamp |
Claim Operations
Claim one or more operations to prevent duplicate processing by other agents.
POST /api/v1/discord-agent/claimRequest Body
{
"ids": [1, 2],
"agent_id": "my-server-agent"
}| Field | Type | Required | Description |
|---|---|---|---|
ids | array | Yes | Array of operation IDs to claim |
agent_id | string | No | Identifier for this agent instance |
Response
{
"success": true,
"data": {
"claimed": [1, 2],
"already_claimed": [],
"not_found": []
},
"message": "Operations claimed successfully"
}Response Fields
| Field | Type | Description |
|---|---|---|
claimed | array | IDs successfully claimed |
already_claimed | array | IDs already claimed by another agent |
not_found | array | IDs that don't exist |
Confirm Operation
Mark an operation as successfully completed.
POST /api/v1/discord-agent/confirm/{id}Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | Operation ID |
Response
{
"success": true,
"message": "Operation confirmed successfully"
}Report Failure
Report that an operation failed.
POST /api/v1/discord-agent/fail/{id}Request Body
{
"error": "Member not found in guild. They may have left the server."
}| Field | Type | Required | Description |
|---|---|---|---|
error | string | Yes | Error message describing the failure |
Response
{
"success": true,
"message": "Failure recorded"
}Failed operations are retried automatically after a delay, with exponential backoff.
Get Operation Status
Check the status of a specific operation.
GET /api/v1/discord-agent/status/{id}Response
{
"success": true,
"data": {
"id": 1,
"status": "completed",
"operation": "assign",
"guild_id": "123456789012345678",
"discord_user_id": "987654321098765432",
"role_id": "111222333444555666",
"claimed_at": "2025-01-20T14:30:05Z",
"completed_at": "2025-01-20T14:30:07Z",
"agent_id": "my-server-agent"
}
}Status Values
| Status | Description |
|---|---|
pending | Awaiting processing |
claimed | Claimed by an agent |
completed | Successfully completed |
failed | Failed (will retry) |
cancelled | Cancelled (won't retry) |
Error Responses
401 Unauthorized
{
"success": false,
"error": "Unauthorized",
"message": "Invalid or missing API token"
}403 Forbidden
{
"success": false,
"error": "Forbidden",
"message": "Token does not have discord:agent scope"
}404 Not Found
{
"success": false,
"error": "Not Found",
"message": "Operation not found"
}409 Conflict
{
"success": false,
"error": "Conflict",
"message": "Operation already claimed by another agent"
}429 Too Many Requests
{
"success": false,
"error": "Too Many Requests",
"message": "Rate limit exceeded",
"retry_after": 60
}Rate Limits
Discord Agent endpoints have relaxed rate limits compared to standard API endpoints:
| Endpoint | Limit |
|---|---|
GET /pending | 120 requests/minute |
POST /claim | 60 requests/minute |
POST /confirm/{id} | 120 requests/minute |
POST /fail/{id} | 60 requests/minute |
Polling Best Practices
Recommended Interval
Poll every 30 seconds for a balance between responsiveness and efficiency:
const POLL_INTERVAL = 30 * 1000; // 30 seconds
setInterval(async () => {
const pending = await fetchPending();
if (pending.length > 0) {
await processOperations(pending);
}
}, POLL_INTERVAL);Prevent Duplicate Processing
Always claim operations before processing:
async function processOperations(operations) {
// 1. Claim first
const ids = operations.map(op => op.id);
const claimResult = await claim(ids);
// 2. Only process claimed operations
for (const id of claimResult.claimed) {
const op = operations.find(o => o.id === id);
await processOperation(op);
}
}Handle Errors Gracefully
Report failures so Pixlpay can retry later:
async function processOperation(operation) {
try {
await assignRole(operation);
await confirm(operation.id);
} catch (error) {
await reportFailure(operation.id, error.message);
}
}Example Implementation
Here's a minimal implementation in Node.js:
const axios = require('axios');
const api = axios.create({
baseURL: 'https://yourstore.pixlpay.net/api/v1/discord-agent',
headers: {
'Authorization': `Bearer ${process.env.PIXLPAY_API_KEY}`,
'Content-Type': 'application/json',
},
});
async function pollForOperations() {
try {
// 1. Fetch pending operations
const { data } = await api.get('/pending');
if (!data.data || data.data.length === 0) {
return; // Nothing to process
}
console.log(`Found ${data.data.length} pending operations`);
// 2. Claim them
const ids = data.data.map(op => op.id);
await api.post('/claim', { ids });
// 3. Process each operation
for (const operation of data.data) {
await processOperation(operation);
}
} catch (error) {
console.error('Poll error:', error.message);
}
}
async function processOperation(op) {
try {
// Your Discord role logic here
if (op.operation === 'assign') {
await discordClient.guilds.cache
.get(op.guild_id)
.members.fetch(op.discord_user_id)
.then(member => member.roles.add(op.role_id));
} else {
await discordClient.guilds.cache
.get(op.guild_id)
.members.fetch(op.discord_user_id)
.then(member => member.roles.remove(op.role_id));
}
// Confirm success
await api.post(`/confirm/${op.id}`);
console.log(`Completed operation ${op.id}`);
} catch (error) {
// Report failure
await api.post(`/fail/${op.id}`, { error: error.message });
console.error(`Failed operation ${op.id}:`, error.message);
}
}
// Poll every 30 seconds
setInterval(pollForOperations, 30000);
pollForOperations(); // Initial pollWebhook Alternative
If you prefer webhooks over polling, you can configure a webhook endpoint to receive role operations in real-time:
- Go to Settings > Webhooks
- Create a webhook with these events:
discord.role.assigndiscord.role.remove
See Webhook Events for payload formats.
However, the polling approach is recommended because:
- More reliable (no missed webhooks)
- Works behind firewalls
- Handles rate limits gracefully
- Built-in retry logic
Related Documentation
- Discord Integration Guide - Setup instructions
- Authentication - API token setup
- Plugin Documentation - Full Discord plugin docs
