# Postback Data

When you set a postback URL, after each transaction we’ll send a POST request with a JSON body in the following shape:

```json
{
  "postback_secret": "xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "postback_type": "transaction",
  "tx_id": 67890,
  "tx_hash": "abcdef1234567890",
  "tx_type": "invoice",
  "label": "Order #1234",
  "currency": "USDTBEP20",
  "amount": 1000.00,
  "amount_usd": 1000.00,
  "network_fee": 0.00,
  "network_fee_usd": 0.00,
  "service_fee": 5.00,
  "service_fee_usd": 5.00,
  "received_amount": 500.00,
  "received_amount_usd": 500.00,
  "status": "paid",
  "created_at": "2025-05-24T15:00:00Z"
}
```

{% hint style="warning" %}
Note that received\_amount may be less than amount if the client has not paid the entire amount at once. For automatic reconciliation of the payment status, see the is\_paid field.
{% endhint %}

#### Field Reference

<table><thead><tr><th width="215">Field</th><th width="73">Type</th><th>Description</th></tr></thead><tbody><tr><td>postback_secret</td><td>string</td><td>Your postback secret from profile page</td></tr><tr><td>postback_type</td><td>string</td><td>Postback type (transaction, wallet_expired, invoice_expired)</td></tr><tr><td>tx_id</td><td>int</td><td>Internal transaction ID in our system</td></tr><tr><td>tx_hash</td><td>string</td><td>On-chain transaction hash</td></tr><tr><td>tx_type</td><td>string</td><td>invoice (payment to an invoice) or wallet (direct wallet transfer)</td></tr><tr><td>label</td><td>string</td><td>Your custom label passed in when creating the invoice or wallet</td></tr><tr><td>currency</td><td>string</td><td>Currency code; here <code>USDTBEP20</code></td></tr><tr><td>amount</td><td>float</td><td>Amount in the original currency (before fees)</td></tr><tr><td>amount_usd</td><td>float</td><td>USD equivalent at the time of invoice creation (for stablecoins this will typically match)</td></tr><tr><td>network_fee</td><td>float</td><td>Network fee of a currency</td></tr><tr><td>network_fee_usd</td><td>float</td><td>USD equivalent of the network fee</td></tr><tr><td>service_fee</td><td>float</td><td>Our service fee</td></tr><tr><td>service_fee_usd</td><td>float</td><td>USD equivalent of the service fee</td></tr><tr><td>received_amount</td><td>float</td><td>Amount received for all payments per invoice (For invoices only)</td></tr><tr><td>received_amount_usd</td><td>float</td><td>USD equivalent of the net received amount</td></tr><tr><td>status</td><td>string</td><td>Transaction status. Possible values: paid, partially_paid</td></tr><tr><td>created_at</td><td>string</td><td>ISO 8601 timestamp when the transaction was created</td></tr></tbody></table>

**Example**

{% tabs %}
{% tab title="Python" %}
**Flask**

```python
from flask import Flask, request, jsonify, abort
import os

app = Flask(__name__)
SHARED_SECRET = os.environ.get("POSTBACK_SECRET")

@app.route('/postback', methods=['POST'])
def postback():
    data = request.get_json()

    # Verify that the incoming secret matches
    if data.get("postback_secret") != SHARED_SECRET:
        abort(401)

    # Remove secret before further processing
    data.pop("postback_secret", None)

    # TODO: handle the transaction data
    # e.g. save to database, update order status, etc.

    return jsonify({"status": "ok"}), 200
```

**FastAPI**

```python
from fastapi import FastAPI, Request, HTTPException
import os

app = FastAPI()
SHARED_SECRET = os.getenv("POSTBACK_SECRET")

@app.post("/postback")
async def postback(request: Request):
    payload = await request.json()

    # verify the shared secret
    if payload.get("postback_secret") != SHARED_SECRET:
        raise HTTPException(status_code=401, detail="Unauthorized")

    # remove secret before processing
    payload.pop("postback_secret", None)

    # TODO: handle transaction data
    return {"status": "ok"}
```

**Django**

```python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('postback/', views.postback),
]

# views.py
import os, json
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt

SHARED_SECRET = os.getenv("POSTBACK_SECRET")

@csrf_exempt
def postback(request):
    if request.method != "POST":
        return HttpResponse(status=405)

    try:
        payload = json.loads(request.body)
    except json.JSONDecodeError:
        return HttpResponse(status=400)

    # verify the shared secret
    if payload.get("postback_secret") != SHARED_SECRET:
        return HttpResponse(status=401)

    # remove secret
    payload.pop("postback_secret", None)

    # TODO: handle transaction data
    return JsonResponse({"status": "ok"})
```

{% endtab %}

{% tab title="PHP" %}
**php\://input**

```php
<?php
// /postback.php

define('SHARED_SECRET', getenv('POSTBACK_SECRET'));

$raw = file_get_contents('php://input');
$data = json_decode($raw, true);

// Verify that the incoming secret matches
if (!isset($data['postback_secret']) || $data['postback_secret'] !== SHARED_SECRET) {
    http_response_code(401);
    exit;
}

// Remove secret before further processing
unset($data['postback_secret']);

// TODO: handle the transaction data
// e.g. insert into database, mark invoice paid, etc.

http_response_code(200);
echo json_encode(['status' => 'ok']);
```

**Laravel**

```php
// routes/api.php
Route::post('/postback', 'PostbackController@handle');

// app/Http/Controllers/PostbackController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostbackController extends Controller
{
    public function handle(Request $request)
    {
        $secret = $request->input('postback_secret');

        // verify the shared secret
        if ($secret !== env('POSTBACK_SECRET')) {
            return response()->json(['message' => 'Unauthorized'], 401);
        }

        // remove secret from payload
        $data = $request->except('postback_secret');

        // TODO: handle $data transaction
        return response()->json(['status' => 'ok']);
    }
}
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const http = require('http');
const SHARED_SECRET = process.env.POSTBACK_SECRET;

const server = http.createServer((req, res) => {
  if (req.method === 'POST' && req.url === '/postback') {
    let body = '';
    req.on('data', chunk => body += chunk);
    req.on('end', () => {
      let data;
      try {
        data = JSON.parse(body);
      } catch {
        res.writeHead(400);
        return res.end();
      }

      // Verify that the incoming secret matches
      if (data.postback_secret !== SHARED_SECRET) {
        res.writeHead(401);
        return res.end();
      }

      // Remove secret before further processing
      delete data.postback_secret;

      // TODO: handle the transaction data
      // e.g. update your records, trigger business logic, etc.

      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ status: 'ok' }));
    });
  } else {
    res.writeHead(404);
    res.end();
  }
});

server.listen(8000, () => console.log('Listening on port 8000'));
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const express = require('express');
const app = express();
const SHARED_SECRET = process.env.POSTBACK_SECRET;

app.use(express.json());

app.post('/postback', (req, res) => {
  const data = req.body;

  // Verify that the incoming secret matches
  if (data.postback_secret !== SHARED_SECRET) {
    return res.sendStatus(401);
  }

  // Remove secret before further processing
  delete data.postback_secret;

  // TODO: handle the transaction data
  // e.g. mark invoice as paid, send notification, etc.

  res.json({ status: 'ok' });
});

app.listen(8000, () => {
  console.log('Server running on http://localhost:8000');
});
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
# config/routes.rb
Rails.application.routes.draw do
  post '/postback', to: 'postbacks#create'
end

# app/controllers/postbacks_controller.rb
class PostbacksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    secret = params.delete(:postback_secret)

    # verify the shared secret
    return head :unauthorized unless secret == ENV['POSTBACK_SECRET']

    # params now holds your transaction data
    # TODO: handle params (tx_hash, is_paid, etc.)

    render json: { status: 'ok' }
  end
end
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "os"
)

func main() {
    r := gin.Default()
    shared := os.Getenv("POSTBACK_SECRET")

    r.POST("/postback", func(c *gin.Context) {
        var payload map[string]interface{}
        if err := c.ShouldBindJSON(&payload); err != nil {
            c.Status(http.StatusBadRequest)
            return
        }

        // verify the shared secret
        if ps, ok := payload["postback_secret"].(string); !ok || ps != shared {
            c.Status(http.StatusUnauthorized)
            return
        }

        // remove the secret
        delete(payload, "postback_secret")

        // TODO: process payload (tx_id, received_amount, etc.)
        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })

    r.Run(":8000")
}
```

{% endtab %}
{% endtabs %}
