Octopus Cards

Products API

List products or retrieve a single product by ID

GET /api/v1/products

Browse the product catalog. Returns a paginated list of products available to your client account, filtered by country, currency, category, or search term.

Request

curl "{{host}}/api/v1/products?country_id=1&category=Gaming&page=1&limit=25" \
  -H "Authorization: Bearer <token>"
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type Denomination struct {
    MinValue *float64 `json:"min_value"`
    MaxValue *float64 `json:"max_value"`
    Discount *float64 `json:"discount"`
}

type Product struct {
    ID                     int             `json:"id"`
    Name                   string          `json:"name"`
    Category               string          `json:"category"`
    SubCategory            *string         `json:"sub_category"`
    CountryCode            string          `json:"country_code"`
    CurrencyCode           string          `json:"currency_code"`
    ImageURL               *string         `json:"image_url"`
    DeliveryMode           *string         `json:"delivery_mode"`
    DeliveryTime           *string         `json:"delivery_time"`
    Validity               *string         `json:"validity"`
    AvailableDenominations []Denomination  `json:"available_denominations"`
}

func main() {
    req, _ := http.NewRequest("GET", "{{host}}/api/v1/products?country_id=1&limit=25", nil)
    req.Header.Set("Authorization", "Bearer <token>")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("Total:", resp.Header.Get("X-Total-Count"))

    var products []Product
    json.NewDecoder(resp.Body).Decode(&products)

    for _, p := range products {
        fmt.Printf("%s (%s) — %s\n", p.Name, p.CurrencyCode, p.Category)
    }
}

Query Parameters

KeyTypeDefaultDescription
pageinteger1Page number (1-based)
limitinteger10Items per page (1–500)
categorystringFilter by category name (exact match)
country_idintegerFilter by country ID
currency_idintegerFilter by currency ID
searchstringSearch by product name (case-insensitive)
sort_bystringnameSort field: name, category
sort_dirstringascSort direction: asc or desc

Response

[
  {
    "id": 123,
    "name": "Steam Wallet Card",
    "category": "Gaming",
    "sub_category": "PC Gaming",
    "country_code": "USA",
    "currency_code": "USD",
    "image_url": "https://cdn.example.com/steam.png",
    "delivery_mode": "Code with PIN",
    "delivery_time": "Instant",
    "validity": "12 months",
    "available_denominations": [
      {
        "min_value": 50.00,
        "max_value": 50.00,
        "discount": 3.5
      },
      {
        "min_value": 100.00,
        "max_value": 100.00,
        "discount": 3.5
      }
    ]
  }
]

Response Headers

HeaderDescription
X-PageCurrent page number
X-Per-PageItems per page
X-Total-CountTotal matching products
X-Total-PagesTotal pages
X-Page-SizeItems in current page
X-Has-Moretrue if more pages exist

Response Fields

KeyTypeDescription
idintegerUnique product identifier
namestringProduct name (e.g. "Steam Wallet Card")
categorystringPrimary category (e.g. "Gaming", "Gift Cards")
sub_categorystring or nullSecondary category (e.g. "PC Gaming")
country_codestringISO 3166-1 alpha-3 country code (e.g. USA)
currency_codestringISO 4217 currency code (e.g. USD)
image_urlstring or nullProduct image URL
delivery_modestring or nullCode with PIN or URL
delivery_timestring or nullInstant or Delayed
validitystring or nullVoucher validity period (e.g. "12 months")
available_denominationsarrayList of available denominations with pricing
available_denominations[].min_valuenumberMinimum face value
available_denominations[].max_valuenumberMaximum face value (equals min_value for fixed denominations)
available_denominations[].discountnumberDiscount percentage off face value

Errors

400 Bad Request — Invalid query parameters.

{
  "error": {
    "name": "BadRequestError",
    "code": "BAD_REQUEST",
    "message": "Invalid query parameters"
  }
}

401 Unauthorized — Missing or invalid JWT token.

{
  "error": {
    "name": "UnauthorizedError",
    "code": "UNAUTHORIZED",
    "message": "Authorization header required"
  }
}

403 Forbidden — Request IP not in whitelist.

{
  "error": {
    "name": "ForbiddenError",
    "code": "FORBIDDEN",
    "message": "IP address not authorized"
  }
}

400 Bad Request — Vouchers feature not enabled for your client.

{
  "error": {
    "name": "BadRequestError",
    "code": "INVALID_FEATURE",
    "message": "The requested feature is not enabled for this client"
  }
}

500 Internal Server Error — Database query failed.

{
  "error": {
    "name": "InternalServerError",
    "code": "INTERNAL_SERVER_ERROR",
    "message": "Failed to retrieve products"
  }
}

GET /api/v1/products/:id

Returns full details for a single product, including all available denominations and their discounts.

Request

curl "{{host}}/api/v1/products/123" \
  -H "Authorization: Bearer <token>"
req, _ := http.NewRequest("GET", "{{host}}/api/v1/products/123", nil)
req.Header.Set("Authorization", "Bearer <token>")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    panic(err)
}
defer resp.Body.Close()

var product Product
json.NewDecoder(resp.Body).Decode(&product)

fmt.Printf("%s%s %s\n", product.Name, product.CurrencyCode, product.DeliveryTime)
for _, d := range product.AvailableDenominations {
    fmt.Printf("  $%.2f–$%.2f (%.1f%% off)\n", *d.MinValue, *d.MaxValue, *d.Discount)
}

Request Parameters

KeyTypeRequiredDescription
idintegerYesProduct ID (path parameter). Must be greater than 0.

Response

{
  "id": 123,
  "name": "Steam Wallet Card",
  "category": "Gaming",
  "sub_category": "PC Gaming",
  "country_code": "USA",
  "currency_code": "USD",
  "image_url": "https://cdn.example.com/steam.png",
  "terms": "Non-refundable. Redeemable on Steam only.",
  "details": "Add funds to your Steam wallet for games, DLC, and in-game items.",
  "how_to_use": "Open Steam client → Account Details → Add Funds → Redeem code",
  "delivery_mode": "Code with PIN",
  "delivery_time": "Instant",
  "validity": "12 months",
  "available_denominations": [
    {
      "min_value": 10.00,
      "max_value": 10.00,
      "discount": 3.0
    },
    {
      "min_value": 50.00,
      "max_value": 50.00,
      "discount": 3.5
    },
    {
      "min_value": 100.00,
      "max_value": 100.00,
      "discount": 3.5
    }
  ]
}

Response Fields

All fields from the list endpoint, plus:

KeyTypeDescription
termsstring or nullRedemption terms and conditions
detailsstring or nullProduct description
how_to_usestring or nullStep-by-step redemption instructions

Errors

400 Bad Request — ID is not a valid integer.

{
  "error": {
    "name": "BadRequestError",
    "code": "BAD_REQUEST",
    "message": "Invalid product ID"
  }
}

404 Not Found — Product does not exist or is blacklisted for your client.

{
  "error": {
    "name": "NotFoundError",
    "code": "NOT_FOUND",
    "message": "Product not found"
  }
}

401 Unauthorized — Missing or invalid JWT token.

{
  "error": {
    "name": "UnauthorizedError",
    "code": "UNAUTHORIZED",
    "message": "Authorization header required"
  }
}

403 Forbidden — Request IP not in whitelist.

{
  "error": {
    "name": "ForbiddenError",
    "code": "FORBIDDEN",
    "message": "IP address not authorized"
  }
}

On this page