Webhook

Overview

When an event occurs on the platform, CatFee will send an HTTP POST request to your configured callback address (callbackUrl). You need to:

  1. Receive and parse the JSON data.

  2. Identify the event type via the X-EVENT-TYPE header.

  3. Return HTTP 200 after successful processing.

  4. Otherwise, the platform will retry according to its retry policy.


Configure the Callback URL

  • Location: Go to User Center → API Settings.

  • Configuration: Fill in callbackUrl and check the event types you want to subscribe to.

  • Protocol: Both HTTP and HTTPS are supported.

  • Identification: The event type is indicated by the request header X-EVENT-TYPE.

  • Note: Dynamic subscription via API is not supported.


Request Headers

Each webhook request includes the following HTTP headers:

Header Name
Description

X-EVENT-ID

Unique event ID (idempotency key)

X-EVENT-TYPE

Event type (enum EventType)

X-EVENT-VERSION

Event version number


Event Structure (JSON)

The request body of a webhook is JSON, using snake_case for top-level fields:

{
  "event_type": "EVENT_BALANCE",
  "event_id": "aabbccdd-1122-3344-5566-77889900",
  "data": { /* event-specific payload */ }
}

Event Types (EventType)

Currently supported types: EVENT_BALANCE, EVENT_TRON_MATE_SUBSCRIPTION, EVENT_DELEGATION (If you receive an unknown type, record and ignore it.)

Example: Balance Change (EVENT_BALANCE)

{
  "event_type": "EVENT_BALANCE",
  "event_id": "aabbccdd-1122-3344-5566-77889900",
  "data": {
    "balance_type": "BALANCE_CHANGE_TRANSFER",
    "billing_type": "BILLING_ENERGY",
    "coin_type": "USDT",
    "amount_sun": 1000000,
    "balance": 500000000,
    "balance_usdt": 2000000,
    "timestamp": 1760505600,
    "remark": "transfer in"
  }
}

Example: Tron Mate Subscription (EVENT_TRON_MATE_SUBSCRIPTION)

{
  "event_type": "EVENT_TRON_MATE_SUBSCRIPTION",
  "event_id": "22334455-6677-8899-aabb-ccddeeff",
  "data": {
    "payment_amount_sun": 5000000,
    "payment_timestamp": 1760505600,
    "subscribe_type": "SUBSCRIBE_PRO",
    "address": "TGxxx..."
  }
}

Event Data Definitions


Request Format

  • Method: POST

  • Content-Type: application/json; charset=utf-8

  • Headers: Must include X-EVENT-ID, X-EVENT-TYPE, X-EVENT-VERSION

Example Request

POST /callback HTTP/1.1
Host: example.com
Content-Type: application/json
X-EVENT-ID: aabbccdd-1122-3344-5566-77889900
X-EVENT-TYPE: EVENT_BALANCE
X-EVENT-VERSION: 2025-01-01

{
  "event_type": "EVENT_BALANCE",
  "event_id": "aabbccdd-1122-3344-5566-77889900",
  "data": {
    "balance_type": "BALANCE_CHANGE_TRANSFER",
    "billing_type": "BILLING_ENERGY",
    "coin_type": "USDT",
    "amount_sun": 1000000,
    "balance": 500000000,
    "balance_usdt": 2000000,
    "timestamp": 1760505600,
    "remark": "transfer in"
  }
}

Event Data Definitions

The DelegationEvent object

Describes TRX delegation details (energy or bandwidth). (Full schema as in the OpenAPI JSON definition provided in the original doc.)

The BalanceEvent object

Describes account balance changes, including recharge, transfer, dApp consumption, API billing, refund, and others.

The TronMateSubscriptionEvent object

Describes subscription information for Tron Mate, including payment amount, payment time, subscription type (BASIC or PRO), and address.


Response Requirements

⚠️ NOTICE: Please return success to inform the notification server that you have successfully received and handled the event. Once the notification server receives a success response, it stops retrying.

  • After successful handling, you must return:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

success
  • The platform treats HTTP 200 as success; otherwise, it will retry.


Retry Mechanism

If your endpoint does not return status 200, the system retries the notification.

Retry Schedule

Up to 10 attempts, with the following intervals:

0s / 15s / 30s / 3m / 10m / 20m / 30m / 60m / 3h / 6h

After 10 consecutive failures, the system stops retrying and logs the failure.


Idempotency Recommendation

  • Use X-EVENT-ID as the idempotency key.

  • If the event has already been processed, immediately return 200.


Example Code

Node.js (Express)

import express from 'express';
const app = express();
app.use(express.json());
const processed = new Set();

app.post('/callback', (req, res) => {
  const eventId = req.header('X-EVENT-ID');
  if (processed.has(eventId)) return res.status(200).type('text/plain').send('success');
  processed.add(eventId);

  const evt = req.body; // { event_type, data }
  // TODO: handle event

  res.status(200).type('text/plain').send('success');
});

app.listen(8080);

Java (Spring Boot)

@RestController
public class WebhookController {
  private final Set<String> processed = Collections.synchronizedSet(new HashSet<>());

  @PostMapping(value = "/callback", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
  public ResponseEntity<String> handle(@RequestHeader("X-EVENT-ID") String id,
                                       @RequestBody Map<String, Object> evt) {
    synchronized (processed) {
      if (processed.contains(id)) return ResponseEntity.ok("success");
      processed.add(id);
    }

    // TODO: handle event logic

    return ResponseEntity.ok("success");
  }
}

Python (FastAPI)

from fastapi import FastAPI, Request, Header, Response
app = FastAPI()
processed = set()

@app.post("/callback")
async def webhook(req: Request, x_event_id: str = Header(None)):
    if x_event_id in processed:
        return Response(content="success", media_type="text/plain")
    processed.add(x_event_id)

    evt = await req.json()
    # TODO: handle event

    return Response(content="success", media_type="text/plain")

FAQ

Q1: Can I return JSON? Yes. As long as the HTTP status is 200, the response format does not matter.

Q2: Does HTTP work? Yes, both HTTP and HTTPS are supported.

Q3: Can I subscribe dynamically? No. Subscriptions can only be configured in the User Center.

Q4: How to handle different event types? Use the X-EVENT-TYPE header to branch logic.

Q5: Are events ordered? The system tries to preserve order for the same event type, but it’s not guaranteed.

Q6: How should I handle event versioning? Use the X-EVENT-VERSION header to select your parsing logic; ignore unknown fields.


Version: v1.1.1 (2025-10-21)

Last updated

Was this helpful?