Skip to main content
CodePlanet Docs

Webhooks

Real-time event notifications

Webhooks allow you to receive real-time notifications when events occur in CodePlanet. Instead of polling the API, webhooks push data to your endpoint when something happens.

Overview

┌────────────────┐                    ┌────────────────┐
│   CodePlanet   │  ───HTTP POST──▶  │  Your Server   │
│     Event      │                    │   (Endpoint)   │
└────────────────┘                    └────────────────┘

When an event occurs (like a payment success or problem completion), CodePlanet sends an HTTP POST request to your configured webhook URL with event data.

Supported Events

EventDescription
payment.completedPayment was successful
payment.failedPayment failed
subscription.createdNew subscription started
subscription.cancelledSubscription cancelled
submission.acceptedProblem solved correctly
certificate.earnedCertificate awarded
streak.milestoneStreak milestone reached (7, 30, 100 days)

Event Payload

All webhooks have this structure:

{
  "id": "evt_abc123",
  "type": "payment.completed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    // Event-specific data
  }
}

Example: Payment Completed

{
  "id": "evt_pay_123",
  "type": "payment.completed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "order_id": "order_abc123",
    "payment_id": "pay_xyz789",
    "amount": 79900,
    "currency": "INR",
    "plan": "pro",
    "user_id": "user_123"
  }
}

Example: Submission Accepted

{
  "id": "evt_sub_456",
  "type": "submission.accepted",
  "created_at": "2024-01-15T11:45:00Z",
  "data": {
    "submission_id": "sub_abc789",
    "problem_slug": "two-sum",
    "language": "python",
    "runtime_ms": 45,
    "memory_mb": 16.2,
    "user_id": "user_123"
  }
}

Setting Up Webhooks

1. Create an Endpoint

Your endpoint must:

  • Accept HTTP POST requests
  • Return 2xx status code within 30 seconds
  • Handle idempotency (same event may be sent multiple times)
// Express.js example
app.post("/webhooks/codeplanet", (req, res) => {
  const event = req.body;
  
  // Verify signature first!
  if (!verifySignature(req)) {
    return res.status(401).send("Invalid signature");
  }
  
  // Process event
  switch (event.type) {
    case "payment.completed":
      handlePaymentCompleted(event.data);
      break;
    case "submission.accepted":
      handleSubmission(event.data);
      break;
  }
  
  res.status(200).send("OK");
});

2. Configure in Dashboard

Go to Settings → Developers → Webhooks and:

  1. Click "Add Endpoint"
  2. Enter your URL
  3. Select events to receive
  4. Copy your webhook secret

3. Verify Signatures

Always verify webhook signatures to ensure authenticity:

import crypto from "crypto";
 
function verifySignature(req: Request): boolean {
  const signature = req.headers["x-codeplanet-signature"];
  const timestamp = req.headers["x-codeplanet-timestamp"];
  const body = JSON.stringify(req.body);
  
  const payload = `${timestamp}.${body}`;
  const expectedSignature = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(payload)
    .digest("hex");
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Security

Signature Verification

Every webhook includes:

  • x-codeplanet-signature: HMAC-SHA256 signature
  • x-codeplanet-timestamp: Unix timestamp

Always verify both to prevent replay attacks.

HTTPS Required

Webhook URLs must use HTTPS in production.

IP Allowlist

Webhooks are sent from these IPs:

  • 34.120.XXX.XXX
  • 35.230.XXX.XXX

(Contact support for current IPs)

Retry Policy

If your endpoint fails (non-2xx response or timeout):

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry24 hours

After 5 retries, the webhook is marked as failed.

Best Practices

1. Respond Quickly

Return 200 immediately, then process asynchronously:

app.post("/webhook", (req, res) => {
  // Acknowledge immediately
  res.status(200).send("OK");
  
  // Process in background
  processWebhookAsync(req.body);
});

2. Handle Duplicates

Webhooks may be delivered multiple times. Use the event id for idempotency:

const processed = await db.webhookEvents.findOne({ eventId: event.id });
if (processed) return; // Already handled

3. Log Everything

Keep logs of all webhook events for debugging:

await db.webhookLogs.insert({
  eventId: event.id,
  type: event.type,
  payload: event,
  receivedAt: new Date(),
});

4. Handle Failures Gracefully

If processing fails, don't return 200. Return 500 to trigger retry.

Testing

Webhook Testing Tool

Use the testing tool in the dashboard to send test events to your endpoint.

Local Development

Use ngrok to expose localhost:

ngrok http 3000

Then use the ngrok URL as your webhook endpoint.

Monitoring

Check webhook delivery status in Settings → Developers → Webhooks → Logs:

  • Successful deliveries ✓
  • Failed attempts ✗
  • Pending retries ⏳

Next Steps