Get a product
curl --request GET \
--url https://api-eu.flexportal.io/v1/products/{productId} \
--header 'Authorization: Bearer <token>' \
--header 'Tenant-ID: <tenant-id>'import requests
url = "https://api-eu.flexportal.io/v1/products/{productId}"
headers = {
"Tenant-ID": "<tenant-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'Tenant-ID': '<tenant-id>', Authorization: 'Bearer <token>'}
};
fetch('https://api-eu.flexportal.io/v1/products/{productId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-eu.flexportal.io/v1/products/{productId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Tenant-ID: <tenant-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-eu.flexportal.io/v1/products/{productId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Tenant-ID", "<tenant-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api-eu.flexportal.io/v1/products/{productId}")
.header("Tenant-ID", "<tenant-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-eu.flexportal.io/v1/products/{productId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Tenant-ID"] = '<tenant-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"productId": "<string>",
"tenantId": "<string>",
"productSku": "<string>",
"name": "<string>",
"specification": "<string>",
"category": "<string>",
"brand": "<string>",
"status": "active",
"createdAt": "<string>",
"updatedAt": "<string>",
"description": "<string>",
"images": [
"<string>"
],
"_sync": {
"source": "manual",
"externalId": "<string>",
"lastSyncedAt": "<string>",
"syncStatus": "synced",
"syncError": "<string>",
"readOnlyFields": [
"<string>"
]
},
"variants": [
{
"variantId": "<string>",
"tenantId": "<string>",
"productId": "<string>",
"productSku": "<string>",
"variantSku": "<string>",
"variantName": "<string>",
"grade": "<string>",
"pricing": [
{
"contractLength": 123,
"monthlyPrice": 123,
"setupFee": 123,
"discount": {
"type": "percentage",
"value": 123,
"description": "<string>"
},
"source": "suggested",
"calculatedAt": "<string>",
"presetUsed": "<string>"
}
],
"status": "active",
"createdAt": "<string>",
"updatedAt": "<string>",
"condition": "<string>",
"colorName": "<string>",
"images": [
"<string>"
],
"listPrice": 123,
"acquisitionCost": 123
}
]
}{
"error": {
"code": "<string>",
"message": "<string>"
}
}Products
Get Product
Retrieves a specific product by ID, including its variants.
GET
/
v1
/
products
/
{productId}
Get a product
curl --request GET \
--url https://api-eu.flexportal.io/v1/products/{productId} \
--header 'Authorization: Bearer <token>' \
--header 'Tenant-ID: <tenant-id>'import requests
url = "https://api-eu.flexportal.io/v1/products/{productId}"
headers = {
"Tenant-ID": "<tenant-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'Tenant-ID': '<tenant-id>', Authorization: 'Bearer <token>'}
};
fetch('https://api-eu.flexportal.io/v1/products/{productId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-eu.flexportal.io/v1/products/{productId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Tenant-ID: <tenant-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-eu.flexportal.io/v1/products/{productId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Tenant-ID", "<tenant-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api-eu.flexportal.io/v1/products/{productId}")
.header("Tenant-ID", "<tenant-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-eu.flexportal.io/v1/products/{productId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Tenant-ID"] = '<tenant-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"productId": "<string>",
"tenantId": "<string>",
"productSku": "<string>",
"name": "<string>",
"specification": "<string>",
"category": "<string>",
"brand": "<string>",
"status": "active",
"createdAt": "<string>",
"updatedAt": "<string>",
"description": "<string>",
"images": [
"<string>"
],
"_sync": {
"source": "manual",
"externalId": "<string>",
"lastSyncedAt": "<string>",
"syncStatus": "synced",
"syncError": "<string>",
"readOnlyFields": [
"<string>"
]
},
"variants": [
{
"variantId": "<string>",
"tenantId": "<string>",
"productId": "<string>",
"productSku": "<string>",
"variantSku": "<string>",
"variantName": "<string>",
"grade": "<string>",
"pricing": [
{
"contractLength": 123,
"monthlyPrice": 123,
"setupFee": 123,
"discount": {
"type": "percentage",
"value": 123,
"description": "<string>"
},
"source": "suggested",
"calculatedAt": "<string>",
"presetUsed": "<string>"
}
],
"status": "active",
"createdAt": "<string>",
"updatedAt": "<string>",
"condition": "<string>",
"colorName": "<string>",
"images": [
"<string>"
],
"listPrice": 123,
"acquisitionCost": 123
}
]
}{
"error": {
"code": "<string>",
"message": "<string>"
}
}Overview
Retrieve a single product by ID, including all its variants. Use this endpoint when you need complete product details including pricing for all configurations.Common Use Cases
- Product Detail Page: Display full product information with all available variants
- Variant Selection: Show customers all available configurations and their pricing
- Order Validation: Verify a variant exists and is active before creating an order
- Pricing Display: Show subscription pricing for different contract lengths
Understanding Variants
Each variant represents a specific product configuration:{
"variantId": "var_abc123",
"sku": "MACBOOK-PRO-16-M3-64GB-BLACK",
"specification": "M3 Max, 64GB RAM, 1TB SSD",
"grade": "A",
"color": "Space Black",
"status": "active",
"listPrice": 3999.00,
"acquisitionCost": 2800.00,
"pricing": {
"6": 249.00,
"12": 189.00,
"24": 149.00,
"36": 129.00
}
}
Variant Fields Explained
| Field | Description |
|---|---|
sku | Unique variant identifier used in orders |
specification | Technical specs (e.g., “M3 Max, 64GB”) |
grade | Condition grade: A (like new) to E (refurbished) |
color | Color option |
status | active or inactive |
listPrice | MSRP/retail price |
acquisitionCost | Your cost to acquire this variant |
pricing | Monthly prices by contract length (months) |
Pricing by Contract Length
Thepricing object maps contract lengths (in months) to monthly subscription prices:
{
"pricing": {
"6": 249.00, // 6-month contract: €249/month
"12": 189.00, // 12-month contract: €189/month
"24": 149.00, // 24-month contract: €149/month
"36": 129.00 // 36-month contract: €129/month
}
}
Longer contracts typically have lower monthly prices. Configure your pricing tiers based on your cost recovery targets.
Grade Classification
Products are classified by condition:| Grade | Description | Typical Use |
|---|---|---|
| A | Like new, minimal wear | Premium subscriptions |
| B | Light wear, fully functional | Standard subscriptions |
| C | Visible wear, fully functional | Budget subscriptions |
| D | Significant wear, functional | Short-term/value subscriptions |
| E | Refurbished/repaired | Budget/secondary market |
Example: Building a Variant Selector
const product = await getProduct('prod_abc123');
// Group variants by specification
const variantsBySpec = {};
for (const variant of product.variants) {
if (variant.status === 'active') {
const spec = variant.specification;
if (!variantsBySpec[spec]) {
variantsBySpec[spec] = [];
}
variantsBySpec[spec].push(variant);
}
}
// Display options to user
for (const [spec, variants] of Object.entries(variantsBySpec)) {
console.log(`\n${spec}:`);
for (const v of variants) {
console.log(` - ${v.color} (${v.grade}): €${v.pricing['24']}/mo for 24mo`);
}
}
Related Endpoints
- List Products - Browse all products
- List Variants - Query variants across products
- Update Product - Modify product details
- Create Order - Create orders using variant SKUs
Authorizations
API key obtained from FlexPortal dashboard
Headers
Your tenant identifier
Path Parameters
The product ID
Response
Product details
Available options:
active, discontinued Show child attributes
Show child attributes
Show child attributes
Show child attributes
Was this page helpful?