Skip to content

Southbound API V2

Our Southbound API V2 allows you to automate the purchase of in-game items using a consistent set of game and player fields.

V2 uses the existing /api/southbound/login endpoint. All other V2 endpoints use the /api/southbound/v2 prefix.

Authentication

Use the token returned by the Login endpoint as a bearer token:

plaintext
Bearer {TOKEN}

Keep the token and secret key on your server. Do not expose them in browser or mobile application code.

Signature

Include an X-Signature header with every authenticated request. Generate the signature using HMAC-SHA256 with the secret key returned by Login and encode the result as a lowercase hexadecimal string.

For requests with a JSON body, join these values with one newline character (\n):

plaintext
HTTP_METHOD
FULL_URL
COMPACT_JSON_BODY

For requests without a body, sign only the method and full URL:

plaintext
HTTP_METHOD
FULL_URL

WARNING

Do not sort the JSON keys. Create one compact JSON string and use the same string when generating the signature and sending the request body.

The full URL includes the query string. Its parameter order and encoding must match the request sent to the API. See Signing API Requests for additional details and troubleshooting.

Code Examples

Each example below includes the body only when one is present.

typescript
import { createHmac } from 'node:crypto';

function generateSignature(
  secret: string,
  method: string,
  url: string,
  body?: string,
): string {
  const parts = [method.toUpperCase(), url];

  if (body) {
    parts.push(body);
  }

  return createHmac('sha256', secret)
    .update(parts.join('\n'), 'utf8')
    .digest('hex');
}
php
<?php

function generateSignature(
    string $secret,
    string $method,
    string $url,
    ?string $body = null,
): string {
    $parts = [strtoupper($method), $url];

    if ($body !== null && $body !== '') {
        $parts[] = $body;
    }

    return hash_hmac('sha256', implode("\n", $parts), $secret);
}
python
import hashlib
import hmac


def generate_signature(
    secret: str,
    method: str,
    url: str,
    body: str | None = None,
) -> str:
    parts = [method.upper(), url]

    if body:
        parts.append(body)

    return hmac.new(
        secret.encode("utf-8"),
        "\n".join(parts).encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;

public class SignatureGenerator {
    public static String generateSignature(
        String secret,
        String method,
        String url,
        String body
    ) throws Exception {
        String data = method.toUpperCase() + "\n" + url;

        if (body != null && !body.isEmpty()) {
            data += "\n" + body;
        }

        Mac hmac = Mac.getInstance("HmacSHA256");
        hmac.init(new SecretKeySpec(
            secret.getBytes(StandardCharsets.UTF_8),
            "HmacSHA256"
        ));

        return HexFormat.of().formatHex(
            hmac.doFinal(data.getBytes(StandardCharsets.UTF_8))
        );
    }
}

Endpoints

Login

POST /api/southbound/login

V2 uses the existing Southbound login endpoint.

Body Parameters
ParameterTypeRequiredDescription
emailstringyesEmail
passwordstringyesPassword
json
{
  "email": "user@example.com",
  "password": "password"
}
Response Fields
FieldTypeDescription
tokenstringAuthentication token
secret_keystringSecret key
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "token": "193|example-token",
    "secret_key": "example-secret-key"
  }
}
json
{
  "status": "error",
  "code": 401,
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Invalid credentials"
  },
  "data": null
}

List Games Requires authentication

GET /api/southbound/v2/games

Returns games available to the authenticated reseller.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
Response Fields
FieldTypeDescription
dataarrayList of Game Object
linksarrayPagination links
metaarrayPagination metadata
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": [
    {
      "type": "game",
      "id": "4319f52b-1b97-439f-b2f3-b24ad70099ae",
      "slug": "ragnarok-origin-global",
      "name": "Ragnarok Origin Global",
      "image": "https://games.oneone.com/storage/games/ragnarok.png",
      "thumbnail": "https://games.oneone.com/storage/games/ragnarok-thumbnail.png"
    }
  ],
  "links": {
    "first": "https://games.oneone.com/api/southbound/v2/games?page=1",
    "last": "https://games.oneone.com/api/southbound/v2/games?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 20,
    "total": 1
  }
}
json
{
  "status": "error",
  "code": 500,
  "error": {
    "code": "LIST_ERROR",
    "message": "Failed to retrieve list of resources",
    "tracking": "e61d7830-ec2f-4c7e-91fc-4cc3bef19865"
  },
  "data": null
}

Show Game Information Requires authentication

GET /api/southbound/v2/games/{game_id}

Returns game information, required input fields, and supported capabilities.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
URL Parameters
ParameterTypeRequiredDescription
game_idstringyesGame ID
Response Fields
FieldTypeDescription
dataobjectGame Object
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "type": "game",
    "id": "4319f52b-1b97-439f-b2f3-b24ad70099ae",
    "slug": "mobile-legends-my",
    "name": "Mobile Legends MY",
    "image": "https://games.oneone.com/storage/games/mobile-legends.png",
    "thumbnail": "https://games.oneone.com/storage/games/mobile-legends-thumbnail.png",
    "fields": [
      {
        "name": "user_id",
        "label": "User ID",
        "type": "string",
        "required": true
      },
      {
        "name": "server_id",
        "label": "Zone ID",
        "type": "string",
        "required": true
      }
    ],
    "capabilities": {
      "account_verification": true,
      "character_selection": false
    }
  }
}
json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}

List Game Servers Requires authentication

GET /api/southbound/v2/games/{game_id}/servers

Returns configured or provider-supplied game servers.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
URL Parameters
ParameterTypeRequiredDescription
game_idstringyesGame ID
Response Fields
FieldTypeDescription
dataarrayList of Game Server
linksarrayPagination links
metaarrayPagination metadata
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": [
    {
      "type": "game_server",
      "id": "1001",
      "name": "Asia"
    }
  ]
}
json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}

List Game Items Requires authentication

GET /api/southbound/v2/games/{game_id}/items

Returns items available to the reseller after applicable pricing plans and discounts.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
URL Parameters
ParameterTypeRequiredDescription
game_idstringyesGame ID
Response Fields
FieldTypeDescription
dataarrayList of Game Item
linksarrayPagination links
metaarrayPagination metadata
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": [
    {
      "type": "game_item",
      "id": 123,
      "name": "100 Diamonds",
      "channels": [
        {
          "channel_name": "OOC",
          "channel_slug": "ooc",
          "currency": "OOC",
          "base_price": "5.00",
          "base_price_cents": 500
        }
      ]
    }
  ]
}
json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}

Verify Account Requires authentication

POST /api/southbound/v2/games/{game_id}/account-verifications

Validates player information and may return selectable characters.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
URL Parameters
ParameterTypeRequiredDescription
game_idstringyesGame ID
Body Parameters
ParameterTypeRequiredDescription
item_idintegeryesGame item ID
Game fieldsmixedvariesRequired fields returned by Show Game Information
json
{
  "item_id": 123,
  "user_id": "841531884",
  "server_id": "asia"
}
Response Fields
FieldTypeDescription
dataobjectAccount Verification object
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "valid": true,
    "account": {
      "id": "841531884",
      "name": null
    },
    "characters": []
  }
}
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "valid": true,
    "account": {
      "id": "841531884",
      "name": null
    },
    "characters": [
      {
        "id": "123456",
        "name": "Warrior",
        "server_id": "1001",
        "server_name": "Asia",
        "level": 80
      }
    ]
  }
}
json
{
  "status": "error",
  "code": 422,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The user id field is required.",
    "errors": {
      "user_id": "The user id field is required."
    }
  },
  "data": null
}

Create Order Requires authentication

POST /api/southbound/v2/orders

Creates a pending order using the reseller's OOC wallet.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
Body Parameters
ParameterTypeRequiredDescription
game_idstringyesGame UUID
item_idintegeryesItem ID belonging to the selected game
voucherstringnoVoucher or promotion code
Game fieldsmixedvariesFields returned by Show Game Information
json
{
  "game_id": "4319f52b-1b97-439f-b2f3-b24ad70099ae",
  "item_id": 123,
  "user_id": "841531884",
  "server_id": "asia",
  "character_id": "123456",
  "character_name": "Warrior"
}
Response Fields
FieldTypeDescription
dataobjectOrder Object
json
{
  "status": "success",
  "code": 201,
  "error": null,
  "data": {
    "type": "order",
    "id": "01J8PWVWFRXRPWGVGP2ANQ90TQ",
    "game_id": "4319f52b-1b97-439f-b2f3-b24ad70099ae",
    "game_name": "Ragnarok Origin Global",
    "item_id": 123,
    "item_name": "100 Diamonds",
    "user_id": "841531884",
    "server_id": "asia",
    "character_id": "123456",
    "character_name": "Warrior",
    "status": "new",
    "total_price": "OOC 5.00",
    "completed_at": null,
    "receipt": null,
    "transaction": {
      "type": "transaction",
      "id": "01J8PWVWFRXRPWGVGP2ANQ90TR",
      "currency": "OOC",
      "amount": "5.00",
      "status": "new",
      "paid_at": null
    }
  }
}
json
{
  "status": "error",
  "code": 422,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The selected item id is invalid.",
    "errors": {
      "item_id": "The selected item id is invalid."
    }
  },
  "data": null
}

Get Order Requires authentication

GET /api/southbound/v2/orders/{order_id}

Returns the latest order and transaction state. An order can only be retrieved by the reseller that created it.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
URL Parameters
ParameterTypeRequiredDescription
order_idstringyesOrder ID
Response Fields
FieldTypeDescription
dataobjectOrder Object
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "type": "order",
    "id": "01J8PWVWFRXRPWGVGP2ANQ90TQ",
    "game_id": "4319f52b-1b97-439f-b2f3-b24ad70099ae",
    "game_name": "Ragnarok Origin Global",
    "item_id": 123,
    "item_name": "100 Diamonds",
    "user_id": "841531884",
    "server_id": "asia",
    "character_id": "123456",
    "character_name": "Warrior",
    "status": "pending",
    "total_price": "OOC 5.00",
    "completed_at": null,
    "receipt": null,
    "transaction": {
      "type": "transaction",
      "id": "01J8PWVWFRXRPWGVGP2ANQ90TR",
      "currency": "OOC",
      "amount": "5.00",
      "status": "new",
      "paid_at": null
    }
  }
}
json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}

Confirm Order Requires authentication

POST /api/southbound/v2/orders/{order_id}/confirm

Charges the reseller wallet and submits the order for fulfillment.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
URL Parameters
ParameterTypeRequiredDescription
order_idstringyesOrder ID
Response Fields
FieldTypeDescription
dataobjectOrder Object
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "type": "order",
    "id": "01J8PWVWFRXRPWGVGP2ANQ90TQ",
    "game_id": "4319f52b-1b97-439f-b2f3-b24ad70099ae",
    "game_name": "Ragnarok Origin Global",
    "item_id": 123,
    "item_name": "100 Diamonds",
    "user_id": "841531884",
    "server_id": "asia",
    "character_id": "123456",
    "character_name": "Warrior",
    "status": "completed",
    "total_price": "OOC 5.00",
    "completed_at": "2026-06-22 12:30:00",
    "receipt": null,
    "transaction": {
      "type": "transaction",
      "id": "01J8PWVWFRXRPWGVGP2ANQ90TR",
      "currency": "OOC",
      "amount": "5.00",
      "status": "paid",
      "paid_at": "2026-06-22 12:30:00"
    }
  }
}
json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}
json
{
  "status": "error",
  "code": 400,
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Insufficient wallet balance"
  },
  "data": null
}

Get Wallet Balance Requires authentication

GET /api/southbound/v2/wallet/balance

Retrieve current wallet balance.

Headers
HeaderTypeRequiredDescription
AuthorizationstringyesBearer token
X-SignaturestringyesSignature
Response Fields
FieldTypeDescription
dataobjectWallet Balance
json
{
  "status": "success",
  "code": 200,
  "error": null,
  "data": {
    "type": "wallet_balance",
    "id": "5fc0e8df-35b5-4c35-b559-fad923501420",
    "currency": "OOC",
    "value_cents": 50000
  }
}
json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}

Response Objects

Game Object

FieldTypeDescription
typestringObject type
idstringGame ID
slugstringGame slug
namestringGame name
imagestringGame image URL
thumbnailstringGame thumbnail URL
fieldsarrayList of Field Object
capabilitiesobjectSupported game capabilities

Field Object

FieldTypeDescription
namestringCanonical request field name
labelstringHuman-readable label
typestringstring, integer, or select
requiredbooleanWhether the field is required
optionsarrayAvailable values when the type is select

Game Server

FieldTypeDescription
typestringObject type
idstringServer ID
namestringServer name

Game Item

FieldTypeDescription
typestringObject type
idintegerItem ID
namestringItem name
channelsarrayList of Game Item Channel

Game Item Channel

FieldTypeDescription
channel_namestringChannel name
channel_slugstringChannel slug
currencystringCurrency
base_pricestringBase price
base_price_centsintegerBase price in cents

Account Verification

FieldTypeDescription
validbooleanWhether the player account is valid
accountobjectVerified account
charactersarrayAvailable player characters

Order Object

FieldTypeDescription
typestringObject type
idstringOrder ID
game_idstringGame ID
game_namestringGame name
item_idintegerItem ID
item_namestringItem name
user_idstringPlayer account ID
server_idstringGame server ID
character_idstringGame character ID
character_namestringGame character name
statusstringOrder status
total_pricestringTotal price
completed_atstringCompletion date
receiptmixedFulfillment receipt
transactionobjectTransaction object

Transaction Object

FieldTypeDescription
typestringObject type
idstringTransaction ID
currencystringTransaction currency
amountstringTransaction amount
statusstringTransaction status
paid_atstringPayment date

Wallet Balance

FieldTypeDescription
typestringObject type
idstringWallet ID
currencystringCurrency
value_centsintegerBalance amount in cents

Additional Notes

Canonical field names

V2 uses consistent public field names even when different game providers use different internal names.

Public fieldMeaning
user_idPlayer, account, or role login identifier
server_idServer, region, or zone identifier
server_nameHuman-readable server name
character_idSelected character or role identifier
character_nameSelected character or role name
character_levelSelected character level

Only send fields returned by the selected game's fields array, plus any selected character values.

Error responses

Common V2 errors:

HTTPError codeDescription
400INSUFFICIENT_FUNDSWallet does not contain enough OOC
401UNAUTHORIZEDInvalid or expired bearer token
403MISSING_HMACX-Signature was not supplied
403INVALID_HMACSignature verification failed
404RESOURCE_NOT_FOUNDResource is missing or belongs to another reseller
422VALIDATION_ERRORRequest fields are invalid
429TOO_MANY_REQUESTSRate limit exceeded
500status-specific codeInternal error; provide tracking to support

Validation example:

json
{
  "status": "error",
  "code": 422,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The user id field is required.",
    "errors": {
      "user_id": "The user id field is required."
    }
  },
  "data": null
}

Not found example:

json
{
  "status": "error",
  "code": 404,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Resource not found"
  },
  "data": null
}

cURL example

List games:

bash
BASE_URL="https://staging.oneone.com"
URL="$BASE_URL/api/southbound/v2/games"

SIGNATURE=$(php -r '
echo hash_hmac("sha256", $argv[1], $argv[2]);
' $'GET\n'"$URL" "$SECRET_KEY")

curl --request GET "$URL" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer $TOKEN" \
  --header "X-Signature: $SIGNATURE"

Create order:

bash
BASE_URL="https://staging.oneone.com"
URL="$BASE_URL/api/southbound/v2/orders"
BODY='{"game_id":"4319f52b-1b97-439f-b2f3-b24ad70099ae","item_id":123,"user_id":"841531884","server_id":"asia"}'

SIGNATURE=$(php -r '
echo hash_hmac("sha256", $argv[1], $argv[2]);
' $'POST\n'"$URL"$'\n'"$BODY" "$SECRET_KEY")

curl --request POST "$URL" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer $TOKEN" \
  --header "X-Signature: $SIGNATURE" \
  --data-raw "$BODY"

V1 and V2

AreaV1V2
Login/api/southbound/loginUses the same login
Resource prefix/api/southbound/*/api/southbound/v2/*
Game fieldsProvider-dependentCanonical names described by fields
Account lookupCharacter-list endpoint with mixed behaviorExplicit account-verification endpoint
Confirm orderTransaction ULID in URLOrder ULID in URL
Order ownershipLegacy behaviorScoped to authenticated reseller

V1 remains available for existing integrations. You can migrate to V2 at your own pace. Test the complete V2 workflow in Sandbox before updating your production integration.