time2pay Developer Docs

Guide

time2pay Payments API

What the platform does, and the shortest path from an API key to a first working deposit.

What this is

A crypto cashier for merchants: players deposit, you get told when the money settled, and you pay winnings back out. Nothing custodial happens on the payout side — the platform cannot move your funds without a signature from your own wallet.

Base URLhttps://time2pay.tech
AuthHMAC-SHA256, server to server
NetworksEVM chains and Tron
AssetsStablecoins configured for your merchant
Your backend
signed call
Cashier API
wallet action
Player wallet
on-chain event
Signed webhook

What you can build

You want toUseNetworks
Take a deposit with no UI of your ownRedirect or iframe to the hosted pageEVM
Take a deposit inside your own cashier UISelf-hosted sessionEVM
Give every player a permanent deposit addressWallet addressEVM and Tron
Pay one player outPayoutEVM and Tron
Pay up to 100 players with one signatureBatch payoutEVM and Tron
Know when money movedWebhooksall
See what your settlement wallet holdsBalanceEVM

Quickstart

The shortest path from nothing to a payment link a player can actually pay.

  1. Get a key. The platform operator issues it in the admin panel: an API_KEY_ID and an API_KEY_SECRET. One active key per merchant — issuing a new one revokes the previous. Keep the secret on your backend; it never belongs in a browser. Without a key you also receive no webhooks, because every callback we send is signed with it.
  2. Copy the client. Signing requests has a complete, dependency-light client for Go, PHP, JavaScript, Python and Node.js — one file each. Every code example in these docs uses it. Set it up once:
    client := &time2pay.Client{
    	BaseURL: os.Getenv("BASE_URL"),
    	KeyID:   os.Getenv("API_KEY_ID"),
    	Secret:  os.Getenv("API_KEY_SECRET"),
    }
    ctx := context.Background()
    $client = new Time2payClient(getenv('BASE_URL'), getenv('API_KEY_ID'), getenv('API_KEY_SECRET'));
    const client = createClient({ baseUrl: env.BASE_URL, keyId: env.API_KEY_ID, secret: env.API_KEY_SECRET });
    client = Time2payClient(os.environ["BASE_URL"], os.environ["API_KEY_ID"], os.environ["API_KEY_SECRET"])
    const client = new Time2payClient({
      baseUrl: process.env.BASE_URL,
      keyId: process.env.API_KEY_ID,
      secret: process.env.API_KEY_SECRET,
    });
  3. Check the signature. Run the client's sign function against the test vector before you debug anything else — a wrong signature and a wrong key look identical from the outside. Then run the smoke test: one read-only call that proves the key, the clock and the IP allowlist.
  4. Create a payment link. One signed call returns a URL. Redirect the player to it and you are done taking a deposit.
    link, err := client.Call(ctx, "POST", "/v1/hpp/sessions", `{
    	"player_id": "player-123",
    	"external_order_id": "deposit-1001",
    	"amount": "125.50",
    	"network": "eth",
    	"token": "USDT",
    	"return_url": "https://casino.example/cashier/return"
    }`)
    if err != nil {
    	return err
    }
    fmt.Println(link["url"])
    $link = $client->call('POST', '/v1/hpp/sessions', [
        'player_id' => 'player-123',
        'external_order_id' => 'deposit-1001',
        'amount' => '125.50',
        'network' => 'eth',
        'token' => 'USDT',
        'return_url' => 'https://casino.example/cashier/return',
    ]);
    echo $link['url'];
    const link = await client.call("POST", "/v1/hpp/sessions", {
      player_id: "player-123",
      external_order_id: "deposit-1001",
      amount: "125.50",
      network: "eth",
      token: "USDT",
      return_url: "https://casino.example/cashier/return",
    });
    console.log(link.url);
    link = client.call("POST", "/v1/hpp/sessions", {
        "player_id": "player-123",
        "external_order_id": "deposit-1001",
        "amount": "125.50",
        "network": "eth",
        "token": "USDT",
        "return_url": "https://casino.example/cashier/return",
    })
    print(link["url"])
    const link = await client.call("POST", "/v1/hpp/sessions", {
      player_id: "player-123",
      external_order_id: "deposit-1001",
      amount: "125.50",
      network: "eth",
      token: "USDT",
      return_url: "https://casino.example/cashier/return",
    });
    console.log(link.url);
    {
      "session": "sess_9f2c1e",
      "token": "E59YRa6MDSiUunx2oZbU1C3-P_jufO18n7ZRj_xeVBE",
      "url": "https://time2pay.tech/p/E59YRa6MDSiUunx2oZbU1C3-P_jufO18n7ZRj_xeVBE",
      "expires_at": "2026-07-28T18:12:25Z"
    }
  5. Credit the player when it settles. We send a signed webhook to your callback URL. Verify it, deduplicate on X-Idempotency-Key and credit exactly once.
Sending external_order_id is what makes a retry safe. Repeating a create with the same reference and the same parameters returns the order you already have instead of opening a second one — and you cannot tell "the request never arrived" from "the response never came back", so you will retry sooner or later.

Where to go next

Choose a flow

Four ways to take a deposit — redirect, iframe, your own UI, or a standing address per player — with the backend and frontend code for each.

Networks & amounts

EVM and Tron differences, address formats, decimals, and how to turn base units into an amount without a float ever touching it.

Payouts

Single and batch withdrawals, the one-time allowance they need, and why a stolen API key still cannot move your money.

Go-live checklist

Everything to tick off before real money flows, and a smoke test that proves your setup in one call.

Service endpoints

Unauthenticated, for your load balancer rather than your integration.

EndpointPurpose
GET /healthzThe process is up. Returns {"status":"ok"}.
GET /readyzDependencies are reachable. 503 means this instance should not receive traffic.