Cosmos+ Developers
Sign out
Cosmos+ Developer Portal

Developer Access

Verify your identity with Discord to manage your applications and webhooks.

Verify with Discord

Access is restricted to authorized developers.

My Applications

Manage your webhook apps and event subscriptions. Up to 2 apps per account.

Loading your apps...

Metrics

Delivery stats and event logs for the last 24 hours.

—
Successful
—
Failed
—
Avg Latency
—
Daily Used
Rate Limits
Daily0 / 70
Monthly0 / 2000
Delivery Activity (24h)
Recent Logs
EventStatusLatencyErrorTime

Select an Application

Choose one of your apps above to view its delivery metrics and event logs.

Documentation

Everything you need to integrate Cosmos+ webhooks securely and correctly.

Getting Started
Reference
Examples

Overview

The Cosmos+ Webhook API lets you receive real-time event notifications directly to any HTTPS endpoint you control. When a relevant event occurs — a moderation action, a giveaway ending, a user gaining XP — Cosmos+ sends a signed HTTP POST to your URL.

You define the path. Use https://your-server.com/api/cosmos, https://worker.example.com/events/any-path, or any public HTTPS URL of your choosing. The path is entirely yours.

Every webhook is signed with HMAC-SHA256. Always verify the signature before processing any payload.

Quickstart

1. Create an App — Navigate to My Apps and click New App. Enter a name and your HTTPS endpoint URL.

2. Save your Secret — After creating the app, your Secret Key is shown once. Copy it immediately and store it securely as an environment variable. It cannot be retrieved again.

3. Subscribe to Events — Expand your app and select the events you want to receive. Guild-level events require Administrator permissions in the target server.

4. Test your integration — Use the Test button to send a ping event and verify your endpoint validates the signature correctly.

Payload Structure

All webhooks share the same JSON envelope. Event-specific data is inside the data field.

{
  "event": "guild.moderation.banned",
  "app_id": "3f4c8b2a-...",
  "timestamp": 1718000000,
  "data": {
    "guild_id": "123456789012345678",
    "user_id":  "987654321098765432",
    "moderator_id": "111222333444555666",
    "reason": "Repeated spam"
  }
}

Signature Verification

Each request includes the X-Cosmos-Signature header — an HMAC-SHA256 hex digest. Compute the expected signature over the raw request body bytes and compare using a constant-time comparison function to prevent timing attacks.

Never skip signature verification. Without it, anyone can send arbitrary fake events to your endpoint.
HMAC-SHA256(key = YOUR_SECRET, message = RAW_REQUEST_BODY_BYTES)

The result is a lowercase hex string. Compare it against the value of X-Cosmos-Signature using a constant-time equals function.

Anti-Replay Protection

Each request includes X-Cosmos-Timestamp (Unix seconds). Reject any request whose timestamp differs from your current time by more than 5 minutes to prevent replay attacks.

import time
ts = int(request.headers['X-Cosmos-Timestamp'])
if abs(time.time() - ts) > 300:
    return "Timestamp out of acceptable range", 401

Event Reference

User Events

These fire only for the owner of the app. They cannot be used to monitor other users.

EventDescriptionKey Fields
user.economy.daily_claimedApp owner claimed their daily reward.amount_claimed, new_balance
user.economy.transfer_receivedCurrency was transferred to the app owner.sender_id, amount, new_balance
user.economy.transfer_sentApp owner sent currency to someone.recipient_id, amount
user.social.rep_receivedApp owner received a reputation point.giver_id, total_rep
user.social.marriage_proposedSomeone proposed marriage to the app owner.proposer_id, message_id
user.premium.purchasedApp owner acquired Cosmos+ Premium.plan, expires_at
user.giveaway.enteredApp owner entered a giveaway.guild_id, message_id, prize
user.giveaway.wonApp owner won a giveaway.guild_id, message_id, prize
user.level.up_globalApp owner leveled up globally.new_level, total_xp
user.roblox.account_linkedApp owner linked their Roblox account.roblox_id, roblox_username

Guild / Server Events

Require Administrator or Owner permissions in the target server. You select which server when subscribing to each event.

EventDescriptionKey Fields
guild.moderation.bannedA user was banned via Cosmos+.user_id, moderator_id, reason
guild.moderation.kickedA user was kicked via Cosmos+.user_id, moderator_id, reason
guild.moderation.timeout_addedA timeout was applied to a user.user_id, moderator_id, duration_seconds
guild.moderation.warn_addedA warning was issued to a user.user_id, moderator_id, reason, warn_id
guild.automod.triggeredAutoMod detected and acted on a message.user_id, rule_broken, action_taken
guild.antilink.triggeredAntilink deleted a prohibited URL from a message.user_id, channel_id, link
guild.triggerword.triggeredA trigger word was matched in a message.user_id, word, channel_id
guild.giveaway.createdA new giveaway was started in the server.message_id, prize, end_time, creator_id
guild.giveaway.endedA giveaway ended and winners were drawn.message_id, prize, winners
guild.suggestion.createdA suggestion was submitted.author_id, content, message_id
guild.suggestion.approvedA suggestion was approved by admins.message_id, approver_id
guild.confession.submittedAn anonymous confession was posted.confession_id, channel_id
guild.apply.createdA user submitted an application form.form_id, response_id, user_id
guild.member.invitedA new member joined via a tracked invite.user_id, inviter_id, invite_code
guild.boost.addedThe server received a Discord Boost.user_id, total_boosts
guild.level_role.awardedA member leveled up and received a role.user_id, new_level, role_id
guild.stats.milestone_reachedThe server hit a member count milestone.milestone, member_count

Request Headers

HeaderDescription
X-Cosmos-SignatureHMAC-SHA256 hex digest of the raw JSON body.
X-Cosmos-EventThe event type string, e.g. guild.moderation.banned.
X-Cosmos-TimestampUnix timestamp (seconds) of dispatch time.
Content-TypeAlways application/json.
User-AgentCosmosWebhooks/1.0

Errors & Retries

Cosmos+ retries on 5xx responses up to 2 additional times with exponential backoff (1s, then 2s). Client errors (4xx) are not retried. Each request has a hard timeout of 5 seconds.

If your endpoint fails 10 consecutive times, the app is automatically suspended. You can re-enable it from your app settings. Always return a 2xx status as fast as possible — offload any heavy processing to a background queue on your side.

Rate Limits

LimitValueNotes
Daily deliveries70 per appResets at midnight UTC.
Monthly deliveries2,000 per appResets on the 1st of each month.
Max apps per account2Contact support for higher limits.
Max subscriptions per app50Across all events and guilds combined.

Node.js / Express

const express = require('express');
const crypto  = require('crypto');

const app    = express();
const SECRET = process.env.COSMOS_SECRET;

app.use(express.json({
  verify: (req, _res, buf) => { req.rawBody = buf; }
}));

// Use any path you want — /your-path, /api/events, anything.
app.post('/your-path', (req, res) => {
  const signature = req.headers['x-cosmos-signature'];
  const timestamp = parseInt(req.headers['x-cosmos-timestamp'], 10);

  if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
    return res.status(401).json({ error: 'Timestamp out of acceptable range' });
  }

  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(req.rawBody)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const { event, data } = req.body;
  console.log('Received:', event, data);
  res.sendStatus(200);
});

app.listen(3000);

Python / FastAPI

import hmac, hashlib, time, os
from fastapi import FastAPI, Request, HTTPException

app    = FastAPI()
SECRET = os.environ["COSMOS_SECRET"].encode()

# Use any path you want — /your-path, /api/events, anything.
@app.post("/your-path")
async def cosmos_webhook(request: Request):
    body      = await request.body()
    signature = request.headers.get("x-cosmos-signature", "")
    timestamp = int(request.headers.get("x-cosmos-timestamp", 0))

    if abs(time.time() - timestamp) > 300:
        raise HTTPException(401, "Timestamp out of acceptable range")

    expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(signature, expected):
        raise HTTPException(401, "Invalid signature")

    payload = await request.json()
    print(f"Event: {payload['event']}", payload["data"])
    return {"ok": True}

Go / net/http

package main

import (
  "crypto/hmac"
  "crypto/sha256"
  "encoding/hex"
  "io"
  "math"
  "net/http"
  "os"
  "strconv"
  "time"
)

var secret = []byte(os.Getenv("COSMOS_SECRET"))

// Use any path you want — /your-path, /api/events, anything.
func handler(w http.ResponseWriter, r *http.Request) {
  body, _ := io.ReadAll(r.Body)

  ts, _ := strconv.ParseInt(r.Header.Get("X-Cosmos-Timestamp"), 10, 64)
  if math.Abs(float64(time.Now().Unix()-ts)) > 300 {
    http.Error(w, "Timestamp out of range", 401)
    return
  }

  mac := hmac.New(sha256.New, secret)
  mac.Write(body)
  expected := hex.EncodeToString(mac.Sum(nil))

  if !hmac.Equal([]byte(r.Header.Get("X-Cosmos-Signature")), []byte(expected)) {
    http.Error(w, "Invalid signature", 401)
    return
  }

  w.WriteHeader(200)
}

func main() {
  http.HandleFunc("/your-path", handler)
  http.ListenAndServe(":8080", nil)
}

Java / Spring Boot

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.HexFormat;

@RestController
public class CosmosWebhookController {

    private final String secret = System.getenv("COSMOS_SECRET");

    // Use any path you want — /your-path, /api/events, anything.
    @PostMapping("/your-path")
    public ResponseEntity<Void> handle(
            @RequestHeader("X-Cosmos-Signature") String signature,
            @RequestHeader("X-Cosmos-Timestamp") long timestamp,
            @RequestBody byte[] body) throws Exception {

        if (Math.abs(System.currentTimeMillis() / 1000L - timestamp) > 300) {
            return ResponseEntity.status(401).build();
        }

        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
        String expected = HexFormat.of().formatHex(mac.doFinal(body));

        if (!MessageDigest.isEqual(signature.getBytes(), expected.getBytes())) {
            return ResponseEntity.status(401).build();
        }

        return ResponseEntity.ok().build();
    }
}
New Application
Must be a public HTTPS URL. Any path works — the choice is yours.
Save your Secret Key
This will never be shown again. Store it securely right now — treat it like a password.

Store this as an environment variable (e.g. COSMOS_SECRET). Use it to verify the X-Cosmos-Signature header on incoming requests.

Delete Application
This will permanently delete the application, all its subscriptions, and all delivery logs. This action cannot be undone.