Skip to documentation

Main navigation

API v1 · Integration guide

Melostore H2H · API v1

H2H API documentation

Start integrating, choose products, and follow every order result.

Base URL https://api.melostore.id/api/v1/h2h

Start integration

Base URL: https://api.melostore.id/api/v1/h2h. All examples use fictional data; replace example SKUs with your account catalogue. There is no separate introduction/authentication endpoint: use GET /profile to check access.

In the dashboard API Credentials menu, click Rotate Keys to generate the API Key, Secret Key, and Webhook Secret. All three are shown once in the generation modal; save them before closing it. Subsequent rotations replace all three immediately, so update both your API credentials and the secret on your webhook receiver.

Import a collection below, select the environment, fill api_key and secret_key from the generated credentials, and choose active SKUs from the regular or Smart catalogue. Keep buyer_trx_id/buyer_bulk_id when retrying a request without a response. Save the returned ID for status checks.

Regular, Smart and bulk create examples use sandbox_mode=true. Sandbox simulates processing/history/callback without debits or financial invoices/email. Dashboard orders are always live. Account checks follow account mode and their own billing; sandbox_mode on order creation does not control account checks.

Authentication, IP, and rate limits

Send Accept: application/json for JSON validation errors; a successful invoice still returns Content-Type application/pdf. Send X-API-Key and X-Secret-Key together on authenticated operations. Obtain credentials from the dashboard; do not expose them in a public browser. GET /check-nickname/games and GET /check-nickname/plan are public. The account must be active and not suspended.

If you use an IP whitelist, register your integration server's public IP in the dashboard before placing live orders. HTTP 403 may indicate that the account or IP is not permitted; check your account settings and server IP.

The general limit follows the tier, shown in X-RateLimit-Limit and X-RateLimit-Remaining. Regular/Smart/category catalogue endpoints also share 20 requests/minute per profile. Account checks use X-Nickname-RateLimit-Limit and X-Nickname-RateLimit-Remaining. If HTTP 429 includes Retry-After, wait that number of seconds; daily allowance resets at 00:00 UTC.

Profile

GET/api/v1/h2h/profile

Authenticated profile and tier. MC/IDR is the wallet currency; USD is a display estimate.

Both headers: X-API-Key and X-Secret-Key

Parameters

No parameters.

Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/profile' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "id": "11111111-1111-4111-8111-111111111111",
    "name": "Example Partner",
    "email": "[email protected]",
    "tier": {
      "name": "Basic",
      "code": "basic",
      "rate_limit": 30,
      "max_ips": 3
    },
    "h2h_balance": 100000,
    "is_sandbox_mode": true,
    "total_volume_30_days": 5000,
    "webhook_url": "https://partner.example.com/h2h-callback",
    "created_at": "2026-10-02T03:00:00+00:00",
    "usd_idr_rate": 17000,
    "display_usd_idr_rate": 17000,
    "display_rate_updated_at": "2026-10-02T03:00:00+00:00"
  }
}

Balance and FX

GET/api/v1/h2h/profile/balance

Reference FX is separate from configured deposit conversion. Nullable USD estimate when reference FX is unavailable.

Both headers: X-API-Key and X-Secret-Key

Parameters

No parameters.

Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/profile/balance' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "h2h_balance": 100000,
    "is_sandbox_mode": true,
    "usd_idr_rate": 17000,
    "display_usd_idr_rate": 17000,
    "display_rate_updated_at": "2026-10-02T03:00:00+00:00",
    "h2h_balance_usd": 5.88
  }
}

Fetch prices for one brand

1. Fetch GET /pricelists/categories for regular brands, or GET /smart-pricelists/categories for Smart brands. Each record is a brand with its parent category object in category and child product types in type. Results are sorted by parent category ID ascending, then brand name A–Z.

2. Choose a data[] entry and use brand_id as the filter: GET /pricelists?brand_id=10&limit=500 or GET /smart-pricelists?brand_id=10&limit=500. Replace 10 with brand_id from your response. In the brand directory, brand_id replaces id; update your integration to read this field.

3. Alternatively, use the brand slug in /pricelists/{category_slug} or /smart-pricelists/{category_slug}. Copy the exact slug from the matching brand list rather than guessing it from a game name.

meta.brands describes the current price page only. Use the brands endpoint for the full list. A listed brand can include out_of_stock products; only order active products.

category is null when the brand has no parent category; these brands appear last. type contains all direct child types sorted by name A–Z, or [] when none exist. A type entry does not guarantee an available product in the selected catalogue.

SKUs, servers, and customer fields

sku_code identifies a regular product for /transaction and bulk. smart_sku_code identifies a Smart product; pass its value as sku_code to /smart-transaction. Both come from the catalogue, independently of account-check game codes.

brand_id and slug come from the brands list. server_code groups products within the catalogue; customer_target_zone identifies the buyer account zone/server according to the product form. Their values need not match.

For the regular catalogue, omit server or leave it empty to include all servers. Omit format or leave it empty for compact; choose legacy to embed inquiry_fields in each product without pagination.

Look up the product inquiry_form_key in meta.inquiry_forms on the same page. Map the first field to customer_target, the second to customer_target_zone when present, and other fields to additional_data using their form keys. For selections, submit value rather than label. Read notice.id or notice.en for extra instructions.

game_code is specific to account checks and comes from games[].game_code in GET /check-nickname/games. Account-check support does not imply that all similar product names share the same zones or conditions.

Pagination and customer details

Fetch the catalogue with limit=500. Pass meta.pagination.next_cursor as cursor on the next request and continue until meta.pagination.has_more=false. The available limit range is 1–1000; use at most 500 per page. This applies to both regular and Smart catalogues.

Follow the selected product's form to enter customer details. In compact format, use inquiry_form_key to find the form in meta.inquiry_forms. Set customer_target from the first field, customer_target_zone from the second when required, and additional_data for other fields using the form's keys. In regular legacy format, the form is available directly in inquiry_fields.

One regular or Smart request creates one order. To repeat a regular SKU for the same target, use bulk order_count (1–100). Bulk orders are processed one at a time in sequence.

Regular catalogue

GET/api/v1/h2h/pricelists

Prices for your account tier. Choose an active sku_code and use POST /transaction. Use bulk order_count for repeated orders. For one brand, pass brand_id from the matching brands list or use its slug in the path. Follow next_cursor until has_more=false.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
limitinteger · queryOptionalItems per page, 1–1000. Use 500 for synchronisation. Without limit, regular responses return all matching products; send limit to paginate. min=1 max=1000
cursorstring · queryOptionalCopy meta.pagination.next_cursor from the previous page. Keep the same filters while paging.
serverstring · queryOptionalProduct catalogue server_code, not the customer account zone. Omit or leave empty to include all catalogue servers.
formatcompact | legacy · queryOptionalOmit or leave empty for compact. legacy embeds inquiry_fields and returns all matching items without cursor pagination.
brand_idinteger · queryOptionalFilter by data[].brand_id from the matching catalogue brands endpoint. Omit to fetch all brands. min=1
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/pricelists?limit=500' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": [
    {
      "name": "Example product",
      "sku_code": "EXAMPLE-10",
      "price": 1000,
      "category_name": "Example category",
      "brand_id": 10,
      "type_name": "Example type",
      "category_order": 1,
      "type_order": 1,
      "server_code": "global",
      "server_name": "Global",
      "status": "active",
      "inquiry_form_key": "example-form",
      "h2h_description": null,
      "description": null,
      "id": 1
    }
  ],
  "meta": {
    "brands": [
      {
        "id": 10,
        "name": "Example brand",
        "thumbnail": null,
        "order": 1
      }
    ],
    "inquiry_forms": {
      "example-form": {
        "fields": [
          {
            "key": "customer_target",
            "name": "Player ID",
            "type": "text"
          },
          {
            "key": "customer_target_zone",
            "name": "Zone",
            "type": "text"
          }
        ],
        "notice": {
          "id": "Masukkan ID dan zone sesuai akun pembeli.",
          "en": "Enter the ID and zone from the buyer account."
        }
      }
    },
    "pagination": {
      "cursor": null,
      "next_cursor": null,
      "limit": 100,
      "total": 1,
      "has_more": false
    }
  }
}

Regular prices by brand

GET/api/v1/h2h/pricelists/{category_slug}

Prices for your account tier. Choose an active sku_code and use POST /transaction. Use bulk order_count for repeated orders. For one brand, pass brand_id from the matching brands list or use its slug in the path. Follow next_cursor until has_more=false.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
category_slugstring · pathRequiredBrand slug from data[].slug in the matching brands endpoint. Existing category/type slugs remain supported.
limitinteger · queryOptionalItems per page, 1–1000. Use 500 for synchronisation. Without limit, regular responses return all matching products; send limit to paginate. min=1 max=1000
cursorstring · queryOptionalCopy meta.pagination.next_cursor from the previous page. Keep the same filters while paging.
serverstring · queryOptionalProduct catalogue server_code, not the customer account zone. Omit or leave empty to include all catalogue servers.
formatcompact | legacy · queryOptionalOmit or leave empty for compact. legacy embeds inquiry_fields and returns all matching items without cursor pagination.
brand_idinteger · queryOptionalFilter by data[].brand_id from the matching catalogue brands endpoint. Omit to fetch all brands. min=1
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/pricelists/example-brand?limit=500' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": [
    {
      "name": "Example product",
      "sku_code": "EXAMPLE-10",
      "price": 1000,
      "category_name": "Example category",
      "brand_id": 10,
      "type_name": "Example type",
      "category_order": 1,
      "type_order": 1,
      "server_code": "global",
      "server_name": "Global",
      "status": "active",
      "inquiry_form_key": "example-form",
      "h2h_description": null,
      "description": null,
      "id": 1
    }
  ],
  "meta": {
    "brands": [
      {
        "id": 10,
        "name": "Example brand",
        "thumbnail": null,
        "order": 1
      }
    ],
    "inquiry_forms": {
      "example-form": {
        "fields": [
          {
            "key": "customer_target",
            "name": "Player ID",
            "type": "text"
          },
          {
            "key": "customer_target_zone",
            "name": "Zone",
            "type": "text"
          }
        ],
        "notice": null
      }
    },
    "pagination": {
      "cursor": null,
      "next_cursor": null,
      "limit": 100,
      "total": 1,
      "has_more": false
    }
  },
  "category": {
    "name": "Example brand",
    "slug": "example-brand"
  }
}

Smart catalogue

GET/api/v1/h2h/smart-pricelists

Prices for your account tier. Choose an active smart_sku_code and use POST /smart-transaction. For one brand, pass brand_id from the matching brands list or use its slug in the path. Follow next_cursor until has_more=false.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
limitinteger · queryOptionalItems per page, 1–1000. Use 500 for synchronisation. Default 500. min=1 max=1000
cursorstring · queryOptionalCopy meta.pagination.next_cursor from the previous page. Keep the same filters while paging.
brandCompatibilityinteger / string · queryOptionalCompatibility alias: brand ID or exact brand name. Prefer brand_id.
categoryCompatibilityinteger / string · queryOptionalCompatibility filter for a root category ID or exact name. Prefer brand_id or a brand slug.
searchstring · queryOptionalSearch product name or Smart SKU.
brand_idinteger · queryOptionalFilter by data[].brand_id from the matching catalogue brands endpoint. Omit to fetch all brands. min=1
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/smart-pricelists?limit=500' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": [
    {
      "name": "Example product",
      "category_name": "Example category",
      "brand_id": 10,
      "type_name": "Example type",
      "category_order": 1,
      "type_order": 1,
      "status": "active",
      "inquiry_form_key": "example-form",
      "h2h_description": null,
      "description": null,
      "smart_sku_code": "smartEXAMPLE",
      "min_price": 1000,
      "max_price": 1000,
      "variant_count": 1,
      "variants": [
        {
          "sku_code": "EXAMPLE-10",
          "price": 1000,
          "status": "active"
        }
      ]
    }
  ],
  "meta": {
    "brands": [
      {
        "id": 10,
        "name": "Example brand",
        "thumbnail": null,
        "order": 1
      }
    ],
    "inquiry_forms": {
      "example-form": {
        "fields": [
          {
            "key": "customer_target",
            "name": "Player ID",
            "type": "text"
          },
          {
            "key": "customer_target_zone",
            "name": "Zone",
            "type": "text"
          }
        ],
        "notice": null
      }
    },
    "pagination": {
      "cursor": null,
      "next_cursor": null,
      "limit": 100,
      "has_more": false
    }
  }
}

Smart prices by brand

GET/api/v1/h2h/smart-pricelists/{category_slug}

Prices for your account tier. Choose an active smart_sku_code and use POST /smart-transaction. For one brand, pass brand_id from the matching brands list or use its slug in the path. Follow next_cursor until has_more=false.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
category_slugstring · pathRequiredBrand slug from data[].slug in the matching brands endpoint. Existing category/type slugs remain supported.
limitinteger · queryOptionalItems per page, 1–1000. Use 500 for synchronisation. Default 500. min=1 max=1000
cursorstring · queryOptionalCopy meta.pagination.next_cursor from the previous page. Keep the same filters while paging.
brandCompatibilityinteger / string · queryOptionalCompatibility alias: brand ID or exact brand name. Prefer brand_id.
categoryCompatibilityinteger / string · queryOptionalCompatibility filter for a root category ID or exact name. Prefer brand_id or a brand slug.
searchstring · queryOptionalSearch product name or Smart SKU.
brand_idinteger · queryOptionalFilter by data[].brand_id from the matching catalogue brands endpoint. Omit to fetch all brands. min=1
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/smart-pricelists/example-brand?limit=500' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": [
    {
      "name": "Example product",
      "category_name": "Example category",
      "brand_id": 10,
      "type_name": "Example type",
      "category_order": 1,
      "type_order": 1,
      "status": "active",
      "inquiry_form_key": "example-form",
      "h2h_description": null,
      "description": null,
      "smart_sku_code": "smartEXAMPLE",
      "min_price": 1000,
      "max_price": 1000,
      "variant_count": 1,
      "variants": [
        {
          "sku_code": "EXAMPLE-10",
          "price": 1000,
          "status": "active"
        }
      ]
    }
  ],
  "meta": {
    "brands": [
      {
        "id": 10,
        "name": "Example brand",
        "thumbnail": null,
        "order": 1
      }
    ],
    "inquiry_forms": {
      "example-form": {
        "fields": [
          {
            "key": "customer_target",
            "name": "Player ID",
            "type": "text"
          },
          {
            "key": "customer_target_zone",
            "name": "Zone",
            "type": "text"
          }
        ],
        "notice": null
      }
    },
    "pagination": {
      "cursor": null,
      "next_cursor": null,
      "limit": 100,
      "has_more": false
    }
  }
}

Regular catalogue brands

GET/api/v1/h2h/pricelists/categories

Brands represented in the regular catalogue for your tier. Each record contains brand_id, name, slug, its parent category, and direct child product types in type[]. Sorted by parent category ID ascending, then brand name A-Z; brands without a parent category appear last. Use brand_id in the catalogue query or slug in /pricelists/{category_slug}. A listed brand or type does not guarantee an active SKU; check the matching pricelist.

Both headers: X-API-Key and X-Secret-Key

Parameters

No parameters.

Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/pricelists/categories' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": [
    {
      "brand_id": 10,
      "name": "Example brand",
      "slug": "example-brand",
      "category": {
        "id": 1,
        "name": "Games",
        "slug": "games"
      },
      "type": [
        {
          "id": 11,
          "name": "Diamonds",
          "slug": "example-diamonds"
        },
        {
          "id": 12,
          "name": "Passes",
          "slug": "example-passes"
        }
      ]
    }
  ]
}

Smart catalogue brands

GET/api/v1/h2h/smart-pricelists/categories

Brands represented in the Smart catalogue for your tier. Each record contains brand_id, name, slug, its parent category, and direct child product types in type[]. Sorted by parent category ID ascending, then brand name A-Z; brands without a parent category appear last. Use brand_id in the catalogue query or slug in /smart-pricelists/{category_slug}. A listed brand or type does not guarantee an active SKU; check the matching pricelist.

Both headers: X-API-Key and X-Secret-Key

Parameters

No parameters.

Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/smart-pricelists/categories' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": [
    {
      "brand_id": 10,
      "name": "Example brand",
      "slug": "example-brand",
      "category": {
        "id": 1,
        "name": "Games",
        "slug": "games"
      },
      "type": [
        {
          "id": 11,
          "name": "Diamonds",
          "slug": "example-diamonds"
        },
        {
          "id": 12,
          "name": "Passes",
          "slug": "example-passes"
        }
      ]
    }
  ]
}

Failed requests and safe retries

Timeout, HTTP 500, or a disconnected request: keep buyer_trx_id and check status using that reference. If needed, retry the same request with the same reference. Do not immediately create a new reference because the first order may have been accepted.

HTTP 401: check both credential headers. HTTP 403: check account access and the applicable IP whitelist. HTTP 422: inspect errors and parameters; MAX_PRICE_TOO_LOW requires a limit covering the catalogue price. HTTP 429: wait Retry-After before retrying.

HTTP 201 only acknowledges receipt. pending/processing means in progress; success means delivered; failed means unsuccessful and deducted funds are refunded. Replace an order only after its previous result is final.

PROVIDER_PRICE_CHANGED: after a failed result, fetch fresh prices before creating a new order. HTTP 409 on bulk may indicate a changed payload using an existing reference, or an unavailable invoice; read the endpoint message.

Need Action & Two-Factor Authentication

Customer actions are independent of transaction status. Regular and Smart orders remain processing during verification: required → submitted → resolved. need_action=true only in required; show a code form only when action.can_submit=true.

When required, collect the buyer verification code and submit POST /api/v1/h2h/transactions/2fa with buyer_trx_id and code. Include the latest action_id and action_version to prevent submission to an outdated request. Code is a trimmed 1–128 character string without control characters; it is not restricted to six digits.

HTTP 202 means receipt for manual admin handling, not proof that the code is valid. Hide the form in submitted and show awaiting admin. Hide the flag in resolved. Admin can request another code: state returns to required with a higher version. Collect a new code from the buyer then.

Use the same authentication headers and IP whitelist. Limit: 5 requests/minute per owner and transaction. Identical-code retries return the previous receipt without another notification; a different code returns 409. Stale versions, final orders, and unavailable actions return 409. A buyer reference belonging to another profile returns 404.

A transaction.action_change webhook can arrive before the final result. Verify X-H2H-Signature on the raw body using the Webhook Secret. Deduplicate by event_id and version, and ignore older action.version values. Store actions separately: action events must never finalize orders, change balances, or trigger refunds. Final result webhooks also include the latest action snapshot.

Status, refunds, and references

HTTP 201/200 means a request was accepted or its status was read. Check data.status for the order result: pending/processing are still in progress, success is delivered, and failed is unsuccessful. Balance deducted for failed orders is refunded. serial_number may be empty until a result is available; account-check details may use null. Read error_code/message for failure details.

Regular orders optionally accept max_price as a positive integer in MC/IDR. It must cover the current price for your tier; otherwise the request returns HTTP 422 with MAX_PRICE_TOO_LOW before any debit. An equal limit is allowed. Omit/null to use automatic pricing.

When max_price is supplied, your balance must cover the full limit, which is held upfront. A successful final charge never exceeds max_price. Unused hold is returned on success; a failed order receives a full refund.

Example: max_price of 1,100 MC holds 1,100 MC upfront. If the final charge is 1,000 MC, 100 MC is returned. These numbers are illustrative; choose your limit using your account catalogue price.

PROVIDER_PRICE_CHANGED means the price changed and the order could not be completed. Once the previous order is failed, fetch current prices and use a new buyer_trx_id to order again. Reusing the failed reference returns the original result.

Regular and Smart orders require buyer_trx_id as your unique order reference. If a request times out, query GET /transaction/{id} using buyer_trx_id or repeat the same request with that reference. A previously used reference returns the original transaction. Use a new reference for a new order. Smart holds max_bid and refunds the difference once the final price is known.

Store the ordered SKU with id and buyer_trx_id. Match status responses and callbacks using id/buyer_trx_id because sku_code in Smart results may differ from the SKU submitted at creation. fulfilled_sku_code identifies the successfully fulfilled SKU. Paid account-check transaction details have null buyer_trx_id and sku_code.

While status is pending/processing, wait for the final result through a callback or status lookup; do not create a replacement for the same request. Transaction responses use price, while callbacks use price_charged. The initial Smart price may show max_bid, and a capped regular order shows its max_price hold before the final price is available. price_charged on a failed order does not represent the final cost after refund; check your balance and invoice for reconciliation.

Create regular order

POST/api/v1/h2h/transaction

Create one regular order using an exact regular sku_code. HTTP 201 acknowledges receipt; read data.status for fulfillment. Repeat the same request with the same buyer_trx_id if the response is uncertain. Use a new reference for a new order. Optional max_price sets your maximum final charge and the full limit is held upfront. Unused hold is refunded on success; failed orders receive a full refund.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
sku_codestring · bodyRequiredExact sku_code from the regular catalogue.
customer_targetstring · bodyRequiredCustomer ID, phone number or email according to the first catalogue inquiry field. Keep it as a string. maxLength=255
customer_target_zonestring | null · bodyOptionalRequired only when the product/game form requires a second zone field. Use its option value, not its label. maxLength=100
additional_dataobject | null · bodyOptionalOther product form values, keyed by the field key in meta.inquiry_forms. Omit when not required.
buyer_trx_idstring · bodyRequiredYour unique order reference. Keep it for retries of the same order; use a new reference for each new order. maxLength=64
sandbox_modeboolean | null · bodyOptionaltrue simulates an order without charging your wallet. false uses live mode. Omit/null uses your account setting.
max_priceinteger | null · bodyOptionalOptional maximum final charge in MC/IDR. Must cover the current price for your tier. Your balance must cover the whole limit, which is held upfront. Unused hold is returned on success; failed orders receive a full refund. Omit/null for automatic pricing. min=1 max=99999999999
Request example
Request example
curl -X POST 'https://api.melostore.id/api/v1/h2h/transaction' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "sku_code": "EXAMPLE-10",
  "customer_target": "123456789",
  "customer_target_zone": "1234",
  "buyer_trx_id": "example-order-001",
  "sandbox_mode": true,
  "additional_data": {
    "email": "[email protected]"
  }
}'
HTTP response:
Response example

Request accepted. Read data.status for the order result.

HTTP 201 · HTTP response
{
  "success": true,
  "message": "Transaction successfully created and queued.",
  "data": {
    "id": "11111111-1111-4111-8111-111111111111",
    "buyer_trx_id": "example-order-001",
    "sku_code": "EXAMPLE-10",
    "product_name": "Example product",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "price": 1000,
    "status": "processing",
    "serial_number": "",
    "webhook_delivery_status": "pending",
    "is_sandbox": true,
    "created_at": "2026-10-02T03:00:00+00:00"
  }
}

Create Smart Order

POST/api/v1/h2h/smart-transaction

Create one Smart order using smart_sku_code as sku_code and max_bid as your budget. The whole budget is held upfront. The successful final charge never exceeds max_bid; unused hold is returned. Failed orders receive a full refund. For PROVIDER_PRICE_CHANGED, refresh the catalogue and only place a new order after the previous order is final.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
sku_codestring · bodyRequiredUse the smart_sku_code value from the Smart catalogue. maxLength=100
customer_targetstring · bodyRequiredCustomer ID, phone number or email according to the first catalogue inquiry field. Keep it as a string. maxLength=255
customer_target_zonestring | null · bodyOptionalRequired only when the product/game form requires a second zone field. Use its option value, not its label. maxLength=100
additional_dataobject | null · bodyOptionalOther product form values, keyed by the field key in meta.inquiry_forms. Omit when not required.
buyer_trx_idstring · bodyRequiredYour unique order reference. Keep it for retries of the same order; use a new reference for each new order. maxLength=64
sandbox_modeboolean | null · bodyOptionaltrue simulates an order without charging your wallet. false uses live mode. Omit/null uses your account setting.
max_bidinteger · bodyRequiredMaximum Smart order budget in MC/IDR. The whole amount is held upfront; unused hold is returned. min=1
Request example
curl -X POST 'https://api.melostore.id/api/v1/h2h/smart-transaction' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "sku_code": "smartEXAMPLE",
  "customer_target": "123456789",
  "customer_target_zone": "1234",
  "buyer_trx_id": "example-smart-order-001",
  "sandbox_mode": true,
  "additional_data": {
    "email": "[email protected]"
  },
  "max_bid": 1100
}'
HTTP response:

Request succeeded.

HTTP 201 · HTTP response
{
  "success": true,
  "message": "Transaction successfully created and queued.",
  "data": {
    "id": "11111111-1111-4111-8111-111111111111",
    "buyer_trx_id": "example-smart-order-001",
    "sku_code": "smartEXAMPLE",
    "product_name": "Example product",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "price": 1100,
    "status": "processing",
    "serial_number": "",
    "webhook_delivery_status": "pending",
    "is_sandbox": true,
    "created_at": "2026-10-02T03:00:00+00:00",
    "max_bid": 1100
  }
}

Transaction status

GET/api/v1/h2h/transaction/{id}

Look up your transaction by UUID or buyer_trx_id. Supports regular, Smart and individual bulk orders; use billing.transaction_id for paid account checks. Match Smart results using id/buyer_trx_id, as sku_code may differ from the ordered SKU. fulfilled_sku_code identifies the successfully fulfilled SKU. Account-check buyer_trx_id, sku_code and serial_number are null. Smart/bulk fields appear only for their order type.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
idstring · pathRequiredOwned transaction UUID or buyer_trx_id
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/transaction/11111111-1111-4111-8111-111111111111' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Order details. Read data.status for the current result.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "id": "11111111-1111-4111-8111-111111111111",
    "buyer_trx_id": "example-order-001",
    "sku_code": "EXAMPLE-10",
    "product_name": "Example product",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "price": 1000,
    "status": "processing",
    "serial_number": "",
    "webhook_delivery_status": "pending",
    "is_sandbox": true,
    "created_at": "2026-10-02T03:00:00+00:00"
  }
}

Submit a two-factor verification code

POST/api/v1/h2h/transactions/2fa

Submit a requested code for manual admin handling, for regular or Smart orders. Requires X-API-Key and X-Secret-Key and the existing IP whitelist. Limit: 5 requests/minute per owner and transaction. HTTP 202 confirms receipt, not validity. Trim code to 1–128 characters without control characters. Retry the same code if delivery is uncertain; a different code is rejected until a new request. This endpoint never creates an order or changes transaction/financial status.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
buyer_trx_idstring · bodyRequiredbuyer_trx_id of the existing order that requests this code. Use the same order reference. maxLength=255
codestring · bodyRequiredLatest verification code from the buyer, trimmed, 1–128 characters without control characters. maxLength=128
action_idstring · bodyOptionalCurrent action.id from this order status or action webhook. Recommended to avoid stale submissions.
action_versioninteger · bodyOptionalCurrent action.version from the same order. Send as a JSON integer. min=1
Request example
curl -X POST 'https://api.melostore.id/api/v1/h2h/transactions/2fa' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "buyer_trx_id": "example-order-001",
  "code": "fictional-alpha-91",
  "action_id": "11111111-1111-4111-8111-111111111111",
  "action_version": 1
}'
HTTP response:

Code accepted for admin handling.

HTTP 202 · HTTP response
{
  "success": true,
  "message": "Your verification code has been received and is awaiting admin handling.",
  "data": {
    "need_action": false,
    "action": {
      "id": "11111111-1111-4111-8111-111111111111",
      "type": "two_factor",
      "state": "submitted",
      "version": 2,
      "message": "Your verification code has been received and is awaiting admin handling.",
      "can_submit": false,
      "updated_at": "2026-10-10T08:02:00+07:00"
    }
  }
}

Bulk order guide

A bulk contains one regular SKU, one target, and 1–100 orders. The price is locked when the order is accepted. Each order has its own reference and result. The full bulk cost is deducted upfront. Ensure your balance covers the whole bulk; insufficient balance prevents its creation.

Use order_count for the number of bulk orders. Omit quantity, max_bid, and order_mode from bulk requests; populated values for these parameters are rejected with HTTP 422.

Use a unique buyer_bulk_id for each bulk and save it before sending the request. The first request returns HTTP 201. Repeating the same request with the same reference returns HTTP 200 without another balance deduction. A different request with the same reference returns HTTP 409. Use a new reference for a new bulk.

Orders within a bulk are processed in sequence. Follow each result in the bulk details; failed orders are refunded. To reorder, use a new transaction reference after the previous result is final. Bulk is available only for the regular catalogue with one SKU and one target; pausing and cancellation are not available.

Read success_count and failed_count for finished orders, and remaining_count for unfinished orders. processing_count counts orders in progress and queued_count counts waiting orders. Bulk statuses are queued, processing, completed, partially_failed, or failed. Use GET /bulk-orders/{id} for the progress summary; callbacks are sent per transaction.

Download the invoice PDF after all orders have finished. HTTP 409 means the invoice is not yet available or the bulk uses sandbox. A live bulk has one invoice summarising costs and refunds, including a full refund and zero final cost if all orders fail. The invoice is emailed after completion. Each order is also available in the Transactions menu. Sandbox does not produce invoices or invoice emails.

AspectRegular transactionBulk order
TransactionsOneorder_count child transactions
Buyer referencebuyer_trx_idbuyer_bulk_id + unique child references
BalanceOne transaction debitAll children debited upfront
ProcessingOne async transactionSequential within a bulk
HistoryTransaction detailProgress summary and child detail
Bulk invoice/emailNot applicableOne document and final email

Create bulk order

POST/api/v1/h2h/bulk-orders

Creates 1–100 separate orders for one regular SKU and one target. The full cost is deducted upfront; insufficient balance prevents creation. Use a unique buyer_bulk_id for each bulk and keep it unchanged on retries. Set order_count and omit quantity, max_bid, and order_mode; populated values for these parameters return HTTP 422.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
sku_codestring · bodyRequiredExact sku_code from the regular catalogue. maxLength=100
customer_targetstring · bodyRequiredCustomer ID, phone number or email according to the first catalogue inquiry field. Keep it as a string. maxLength=255
customer_target_zonestring | null · bodyOptionalRequired only when the product/game form requires a second zone field. Use its option value, not its label. maxLength=100
additional_dataobject | null · bodyOptionalOther product form values, keyed by the field key in meta.inquiry_forms. Omit when not required.
sandbox_modeboolean | null · bodyOptionaltrue simulates an order without charging your wallet. false uses live mode. Omit/null uses your account setting.
buyer_bulk_idstring · bodyRequiredYour unique bulk reference. Identical retries return the original bulk; a changed payload returns HTTP 409. maxLength=64
order_countinteger · bodyRequiredNumber of separate orders, 1–100, processed sequentially for one regular SKU and target. min=1 max=100
Request example
curl -X POST 'https://api.melostore.id/api/v1/h2h/bulk-orders' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "sku_code": "EXAMPLE-10",
  "customer_target": "123456789",
  "customer_target_zone": "1234",
  "sandbox_mode": true,
  "additional_data": {
    "email": "[email protected]"
  },
  "order_count": 3,
  "buyer_bulk_id": "example-bulk-001"
}'
HTTP response:
Response example

Request accepted. Read data.status for the order result.

HTTP 201 · HTTP response
{
  "success": true,
  "message": "Bulk order created and queued.",
  "data": {
    "id": "22222222-2222-4222-8222-222222222222",
    "buyer_bulk_id": "example-bulk-001",
    "sku_code": "EXAMPLE-10",
    "product_name": "Example product",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "order_count": 3,
    "unit_price": 1000,
    "total_debit": 0,
    "status": "queued",
    "via": "api",
    "is_sandbox": true,
    "invoice_available": false,
    "created_at": "2026-10-02T03:00:00+00:00",
    "completed_at": null,
    "total_count": 3,
    "success_count": 0,
    "failed_count": 0,
    "processed_count": 0,
    "remaining_count": 3,
    "processing_count": 0,
    "queued_count": 3
  }
}

Bulk history

GET/api/v1/h2h/bulk-orders

Owned orders, 15 per page. Date filters use Asia/Jakarta inclusive start/end dates.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
pageinteger · queryOptional
statusqueued | processing | completed | partially_failed | failed · queryOptional
searchstring · queryOptionalBuyer reference, target, SKU or product name
start_datestring · queryOptionalYYYY-MM-DD
end_datestring · queryOptionalYYYY-MM-DD, not earlier than start_date
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/bulk-orders?page=1' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Bulk order data and progress.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "current_page": 1,
    "data": [
      {
        "id": "22222222-2222-4222-8222-222222222222",
        "buyer_bulk_id": "example-bulk-001",
        "sku_code": "EXAMPLE-10",
        "product_name": "Example product",
        "customer_target": "123456789",
        "customer_target_zone": "1234",
        "order_count": 3,
        "unit_price": 1000,
        "total_debit": 0,
        "status": "processing",
        "via": "api",
        "is_sandbox": true,
        "invoice_available": false,
        "created_at": "2026-10-02T03:00:00+00:00",
        "completed_at": null,
        "total_count": 3,
        "success_count": 1,
        "failed_count": 1,
        "processed_count": 2,
        "remaining_count": 1,
        "processing_count": 1,
        "queued_count": 0
      }
    ],
    "first_page_url": "https://api.melostore.id/api/v1/h2h/bulk-orders?page=1",
    "from": 1,
    "last_page": 1,
    "last_page_url": "https://api.melostore.id/api/v1/h2h/bulk-orders?page=1",
    "links": [
      {
        "url": null,
        "label": "Previous",
        "active": false
      }
    ],
    "next_page_url": null,
    "path": "https://api.melostore.id/api/v1/h2h/bulk-orders",
    "per_page": 15,
    "prev_page_url": null,
    "to": 1,
    "total": 1
  }
}

Bulk progress

GET/api/v1/h2h/bulk-orders/{id}

Read the bulk status and order counts. processed_count includes successful and failed orders; remaining_count includes orders still waiting or processing.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
idstring · pathRequiredBulk UUID returned by POST /bulk-orders. Use the bulk ID, not buyer_bulk_id or a child transaction ID.
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/bulk-orders/22222222-2222-4222-8222-222222222222' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:

Bulk order data and progress.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "id": "22222222-2222-4222-8222-222222222222",
    "buyer_bulk_id": "example-bulk-001",
    "sku_code": "EXAMPLE-10",
    "product_name": "Example product",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "order_count": 3,
    "unit_price": 1000,
    "total_debit": 0,
    "status": "processing",
    "via": "api",
    "is_sandbox": true,
    "invoice_available": false,
    "created_at": "2026-10-02T03:00:00+00:00",
    "completed_at": null,
    "total_count": 3,
    "success_count": 1,
    "failed_count": 1,
    "processed_count": 2,
    "remaining_count": 1,
    "processing_count": 1,
    "queued_count": 0
  }
}

Bulk child transactions

GET/api/v1/h2h/bulk-orders/{id}/transactions

Sequence-ordered child orders, 15 per page. Each has its own transaction UUID and buyer_trx_id.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
idstring · pathRequiredBulk UUID returned by POST /bulk-orders. Use the bulk ID, not buyer_bulk_id or a child transaction ID.
pageinteger · queryOptional
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/bulk-orders/22222222-2222-4222-8222-222222222222/transactions?page=1' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY"
HTTP response:
Response example

Bulk order data and progress.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "current_page": 1,
    "data": [
      {
        "id": "11111111-1111-4111-8111-111111111111",
        "buyer_trx_id": "BULK-22222222-2222-4222-8222-222222222222-1",
        "bulk_order_id": "22222222-2222-4222-8222-222222222222",
        "buyer_bulk_id": "example-bulk-001",
        "bulk_sequence": 1,
        "sku_code": "EXAMPLE-10",
        "customer_target": "123456789",
        "customer_target_zone": "1234",
        "price": 1000,
        "status": "success",
        "serial_number": "EXAMPLE-SN-001",
        "error_code": null,
        "message": null,
        "created_at": "2026-10-02T03:00:00+00:00",
        "updated_at": "2026-10-02T03:00:00+00:00"
      }
    ],
    "first_page_url": "https://api.melostore.id/api/v1/h2h/bulk-orders/22222222-2222-4222-8222-222222222222/transactions?page=1",
    "from": 1,
    "last_page": 1,
    "last_page_url": "https://api.melostore.id/api/v1/h2h/bulk-orders/22222222-2222-4222-8222-222222222222/transactions?page=1",
    "links": [
      {
        "url": null,
        "label": "Previous",
        "active": false
      }
    ],
    "next_page_url": null,
    "path": "https://api.melostore.id/api/v1/h2h/bulk-orders/22222222-2222-4222-8222-222222222222/transactions",
    "per_page": 15,
    "prev_page_url": null,
    "to": 1,
    "total": 1
  }
}

Download final bulk invoice

GET/api/v1/h2h/bulk-orders/{id}/invoice

Download one PDF summarising the completed bulk's costs and refunds. HTTP 409 means the invoice is not yet available or the bulk uses sandbox. A live bulk where all orders fail shows a full refund and zero final cost.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
idstring · pathRequiredBulk UUID returned by POST /bulk-orders. Use the bulk ID, not buyer_bulk_id or a child transaction ID.
Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/bulk-orders/22222222-2222-4222-8222-222222222222/invoice' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  --fail-with-body --output bulk-invoice.pdf
HTTP response:

Final PDF attachment, stable number H2H-BULK-{UUID}

Content-Type: application/pdf · Content-Disposition: attachment

Account-check billing

See GET /check-nickname/plan for the free allowance and current rates. Nickname and purchase-limit checks share the allowance. Found and not-found results use free allowance or incur a charge when paid checks are enabled on a live account. Every repeated check still uses allowance or incurs a charge.

Found and not-found results include billing.charged_mc, free_remaining_today, and transaction_id when charged. Uncertain verification is not treated as account-not-found. HTTP 402 is insufficient check balance; 429 quota; 404 not found; 422 validation; 502 unavailable. Sandbox account mode uses free allowance only.

Use rates from the latest plan response before offering paid checks. If rates are unavailable, retry before continuing. Example response numbers are illustrative; current rates appear in the account-check plan section.

Current account-check plan

…

Zone references are a snapshot. Check zone requirements through the games endpoint and use the correct game-specific zone code.

Reference

Zone code reference

Choose a game to see the codes used in customer_target_zone. Search by zone name or code.

Game

Supported accounts/games

GET/api/v1/h2h/check-nickname/games

Public list and zone requirements. Zone reference in these docs is a snapshot.

Public, no authentication

Parameters

No parameters.

Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/check-nickname/games' \
  -H 'Accept: application/json'
HTTP response:

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "games": [
    {
      "game_code": "mobile-legends",
      "name": "Mobile Legends",
      "type": "game",
      "zone_required": true,
      "zone_values": null,
      "params": [
        {
          "name": "customer_target"
        },
        {
          "name": "customer_target_zone"
        }
      ],
      "status": "available"
    }
  ]
}

Account-check plan

GET/api/v1/h2h/check-nickname/plan

Public current allowance/rates. Values in examples are fictional snapshots, not an active quote.

Public, no authentication

Parameters

No parameters.

Request example
curl -X GET 'https://api.melostore.id/api/v1/h2h/check-nickname/plan' \
  -H 'Accept: application/json'
HTTP response:

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "free_daily_limit": 40,
    "free_minute_limit": 20,
    "paid_price_mc": 1,
    "paid_minute_limit": 20,
    "resets_at": "00:00 UTC"
  }
}

Check account nickname

POST/api/v1/h2h/check-nickname

Account mode governs billing; no per-request sandbox override. Found and not-found consume allowance or paid charge, including repeated checks.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
game_codestring · bodyRequiredCopy games[].game_code from GET /check-nickname/games. It is separate from a product brand slug. maxLength=100
customer_targetstring · bodyRequiredAccount ID or customer number for the selected game/account check. Send it as a string. maxLength=255
customer_target_zonestring | null · bodyOptionalAccount zone/server according to zone_required and zone_values from GET /check-nickname/games. Required for the Mobile Legends purchase-limit check. maxLength=100
Request example
curl -X POST 'https://api.melostore.id/api/v1/h2h/check-nickname' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "game_code": "mobile-legends",
  "customer_target": "123456789",
  "customer_target_zone": "1234"
}'
HTTP response:
Response example

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "game_code": "mobile-legends",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "username": "Example Player",
    "region": "ID",
    "region_type": "country"
  },
  "billing": {
    "charged_mc": 0,
    "free_remaining_today": 39,
    "transaction_id": null
  }
}

Mobile Legends purchase limit

POST/api/v1/h2h/mobile-legends/purchase-limit

Separate account-check endpoint. Returns per-package flags; do not infer numeric limits from failures. Uses the same billing/quota as nickname.

Both headers: X-API-Key and X-Secret-Key

Parameters

NameType / sourceRequiredDescription
customer_targetstring · bodyRequiredAccount ID or customer number for the selected game/account check. Send it as a string. maxLength=255
customer_target_zonestring · bodyRequiredAccount zone/server according to zone_required and zone_values from GET /check-nickname/games. Required for the Mobile Legends purchase-limit check. maxLength=100
Request example
curl -X POST 'https://api.melostore.id/api/v1/h2h/mobile-legends/purchase-limit' \
  -H 'Accept: application/json' \
  -H "X-API-Key: $H2H_API_KEY" \
  -H "X-Secret-Key: $H2H_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
  "customer_target": "123456789",
  "customer_target_zone": "1234"
}'
HTTP response:

Request succeeded.

HTTP 200 · HTTP response
{
  "success": true,
  "data": {
    "game_code": "mobile-legends",
    "customer_target": "123456789",
    "customer_target_zone": "1234",
    "username": "Example Player",
    "weekly_pass": {
      "limit_reached": false,
      "items": [
        {
          "package_code": "weekly-pass",
          "limit_reached": false
        }
      ]
    },
    "double_diamonds": {
      "eligible": true,
      "items": [
        {
          "package_code": "double-diamonds-50",
          "limit_reached": false
        }
      ]
    }
  },
  "billing": {
    "charged_mc": 0,
    "free_remaining_today": 39,
    "transaction_id": null
  }
}

Regular, Smart, and bulk child callbacks

Enter only the Webhook URL in the dashboard. The Webhook Secret is generated automatically with the API Key and Secret Key through Rotate Keys, shown once, and saved on your webhook receiver. Each rotation replaces all three; update the receiver secret to keep verifying subsequent callbacks.

Final callbacks are sent for success/failed; transaction.action_change events can also arrive while processing. Orders placed manually through the dashboard do not send callbacks. For bulk, callbacks are sent per transaction; use GET /bulk-orders/{id} for the progress summary. The same callback may arrive more than once.

Common fields: id, buyer_trx_id, sku_code, customer_target, customer_target_zone, price_charged, status, serial_number, is_sandbox, created_at, updated_at. Match callbacks to orders using id/buyer_trx_id. For Smart, store the ordered SKU and read fulfilled_sku_code for the successfully fulfilled SKU. Prices use price in transaction responses and price_charged in callbacks; check your balance and invoice for the final cost after refunds.

Only Smart adds max_bid, fulfilled_sku_code (null before success), attempts_count. Only bulk children add bulk_order_id, buyer_bulk_id, bulk_sequence. Failed adds error_code and message when available. Regular callbacks have none of the Smart/bulk additions.

Verify X-H2H-Signature as a hex HMAC-SHA256 over the raw body with webhook_secret. Record action event identity by event_id and version, and final results by id:status before returning 2xx. Ensure repeated deliveries do not process an order or wallet effect twice. Expand the advanced receiver examples below and adapt event storage to your application.

Callback payload example
{
  "id": "11111111-1111-4111-8111-111111111111",
  "buyer_trx_id": "example-order-001",
  "sku_code": "EXAMPLE-10",
  "customer_target": "123456789",
  "customer_target_zone": "1234",
  "status": "success",
  "serial_number": "EXAMPLE-SN-001",
  "is_sandbox": true,
  "created_at": "2026-10-02T03:00:00+00:00",
  "price_charged": 1000,
  "updated_at": "2026-10-02T03:00:00+00:00"
}

Import collections and environment

Download OpenAPI 3.1.0 or Postman Collection v2.1 and the environment template. In Postman choose Import file, import the environment, and select it. Fill api_key/secret_key from Generate/Rotate Keys; template credentials are empty. Save webhook_secret from the same modal on your callback receiver. base_url points to the API without a locale prefix.

Fetch the catalogue first. Set sku_code for regular/bulk, smart_sku_code for Smart, customer_target, zone, and additional_data from the inquiry fields. Set buyer_trx_id for regular, smart_buyer_trx_id for Smart, and buyer_bulk_id for bulk once per order; scripts never rotate them on retry. Create scripts store transaction_id/bulk_order_id for subsequent requests. The matching brands request saves brand_id and category_slug from the first result. For 2FA, first check the correct order status to populate action_buyer_trx_id, action_id, and action_version, then enter the buyer code as two_factor_code.

The callback folder contains saved examples and receiver guidance, with no automatic request to a partner URL. webhook_secret belongs to the receiver, not API authentication. Sandbox bulk invoice requests return 409. For a final live bulk use Send and Download on the invoice operation or the Bulk Order menu.