Skip to main content

Authentication

FlexPortal uses Bearer token authentication. Include your API key in the Authorization header of every request:
curl -X GET "https://api-eu.flexportal.io/v1/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Tenant-ID: YOUR_BUSINESS_UNIT_ID" \
  -H "Content-Type: application/json"
API keys are created in the Dashboard under Settings → API Keys. Each key is scoped to your organization with configurable permissions.

Required Headers

Every API request must include these headers:
HeaderRequiredDescription
AuthorizationYesBearer YOUR_API_KEY
Tenant-IDYesYour Business Unit ID (found in Dashboard → Settings → General)
Content-TypeYes (for POST/PUT/PATCH)application/json

Base URLs

FlexPortal is deployed across multiple regions. Use the base URL closest to your customers:
RegionBase URL
Europehttps://api-eu.flexportal.io
United Stateshttps://api-us.flexportal.io
Qatarhttps://api-qatar.flexportal.io
Data is region-specific. Orders created in the EU region are not visible from US endpoints. Choose your region during onboarding and use it consistently.

Rate Limits

TierRequests per minute
Starter100
GrowthUnlimited*
EnterpriseUnlimited*
*Fair use policy with abuse protection. Sustained high-volume usage may trigger temporary throttling. When rate limited, you’ll receive a 429 Too Many Requests response with a Retry-After header indicating when to retry.

Pagination

List endpoints use cursor-based pagination for reliable, consistent results even when data changes between requests.

Request Parameters

ParameterTypeDefaultDescription
limitinteger50Items per page (max: 100)
startAfterstringCursor from previous response to get next page
sortBystringcreatedAtField to sort by (varies per endpoint)
sortDirstringdescSort direction: asc or desc

Response Structure

{
  "orders": [...],
  "count": 50,
  "limit": 50,
  "hasMore": true,
  "nextCursor": "ord_abc123xyz"
}

Iterating Through Pages

let cursor = null;
let allOrders = [];

do {
  const params = new URLSearchParams({ limit: '100' });
  if (cursor) params.set('startAfter', cursor);

  const response = await fetch(
    `https://api-eu.flexportal.io/v1/orders?${params}`,
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Tenant-ID': 'YOUR_TENANT' } }
  );
  const data = await response.json();

  allOrders.push(...data.orders);
  cursor = data.nextCursor;
} while (cursor);
Use nextCursor value as startAfter for the next request. When hasMore is false or nextCursor is null, you’ve reached the end.

Request Format

All request bodies must be valid JSON:
Content-Type: application/json

Response Format

Success Responses

All successful responses include relevant data and metadata:
{
  "success": true,
  "message": "Order created successfully",
  "orderId": "ord_abc123",
  "order": { ... }
}
For list endpoints:
{
  "orders": [...],
  "count": 25,
  "limit": 50,
  "hasMore": false,
  "nextCursor": null
}

Error Responses

Errors follow a consistent structure with machine-readable codes and human-friendly messages:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": [
      {
        "path": "customer.email",
        "message": "Invalid email format",
        "code": "invalid_string"
      }
    ]
  }
}

HTTP Status Codes

CodeNameDescription
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestInvalid request syntax or parameters
401UnauthorizedMissing or invalid API key
403ForbiddenValid API key but insufficient permissions
404Not FoundResource doesn’t exist
409ConflictResource conflict (e.g., duplicate serial number)
422UnprocessableValidation failed
429Too Many RequestsRate limit exceeded
500Internal ErrorServer error (contact support)

Error Codes Reference

Common error codes you may encounter:
CodeHTTP StatusDescriptionResolution
AUTH_REQUIRED401Missing Authorization headerAdd Authorization: Bearer YOUR_API_KEY header
INVALID_TOKEN401API key is invalid or expiredGenerate a new API key in Dashboard
NO_TENANT_ACCESS403Missing or invalid Tenant-ID headerAdd valid Tenant-ID header
INSUFFICIENT_PERMISSIONS403API key lacks required permissionsUpdate API key permissions in Dashboard
NOT_FOUND404Resource doesn’t existVerify the ID and tenant context
VALIDATION_ERROR400Request body failed validationCheck details array for specific field errors
ASSET_EXISTS409Asset with serial number already existsUse a unique serial number or update existing
VARIANT_IN_USE409Cannot deactivate variant with active ordersComplete or cancel orders using this variant first
ORDER_NOT_CANCELLABLE400Order status doesn’t allow cancellationOnly pending/confirmed orders can be cancelled

Core Resources

FlexPortal’s API is organized around these key resources:
ResourceDescription
ProductsItems in your catalog available for subscription, with variants for different configurations
CustomersEnd users who subscribe to products (individuals or businesses)
OrdersSubscription checkout transactions containing items, addresses, and payment info
SubscriptionsActive subscription contracts tracking billing, payments, and lifecycle events
AssetsIndividual physical units tracked by serial number
PaymentsScheduled and completed billing transactions
Billing GroupsB2B feature for consolidating multiple subscriptions into single invoices
FilesDocuments like contracts, invoices, and uploaded files

Filtering

Most list endpoints support filtering by key fields. Pass filter values as query parameters:
# Get all pending orders for a specific customer
GET /v1/orders?status=pending&customerId=cust_abc123

# Get all active subscriptions
GET /v1/subscriptions?status=active

# Get available assets of a specific SKU
GET /v1/assets?status=available&sku=LAPTOP-PRO-16

Custom Field Filtering (Orders)

If you’ve configured custom fields, filter by them using the customField_ prefix:
GET /v1/orders?customField_siebelId=12345
GET /v1/orders?customField_vendorCode=ABC

Idempotency

For operations that create resources, FlexPortal uses natural idempotency keys:
  • Orders: Customer email + items combination within a short window
  • Assets: Serial number (unique per tenant)
  • Payments: Billing period + subscription ID
If you need to retry a failed request, it’s safe to do so. Duplicate create requests will return the existing resource rather than creating duplicates.

Webhooks

FlexPortal can send real-time notifications for key events. Configure webhook endpoints in the Dashboard under Settings → Webhooks.

Available Events

EventDescription
order.createdNew order submitted
order.fulfilledAll items in order have active subscriptions
subscription.createdNew subscription activated
subscription.extendedSubscription period extended
subscription.upgradedCustomer upgraded to different product
subscription.endedSubscription completed or cancelled
payment.scheduledNew payment scheduled
payment.succeededPayment collected successfully
payment.failedPayment attempt failed
asset.assignedAsset linked to subscription
asset.returnedAsset returned from subscription

Webhook Payload

{
  "event": "subscription.created",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "subscriptionId": "sub_abc123",
    "customerId": "cust_xyz789",
    "orderId": "ord_def456",
    ...
  }
}

SDKs & Libraries

Official SDKs are coming soon. In the meantime, you can use any HTTP client to interact with the API. See our Quickstart Guide for examples in various languages.

Need Help?