Sub-Accounts API
Overview
The Sub-Accounts feature enables SaaS companies to programmatically manage multiple client integrations under one master account. Instead of requiring each of your customers to create their own PSRESTful account, you can create and manage sub-accounts on their behalf, providing seamless PromoStandards API access through your platform.
Use Case Example: SaaS Platform Integration
Scenario: You run a SaaS platform that provides promotional products ordering capabilities to your customers.
Without Sub-Accounts:
- Each customer needs their own PSRESTful account
- Customers manage their own API keys
- No centralized control or visibility
- Complex onboarding process
With Sub-Accounts:
- Your platform has one parent account (Enterprise plan)
- Create a sub-account for each of your customers
- Generate API keys for each sub-account
- Map sub-accounts to your customers via
external_customer_id - Make PromoStandards API calls on behalf of your customers
- Track usage and manage access centrally
Getting Started
Prerequisites
Parent Account Requirements:
- Enterprise plan
- Sub-accounts feature enabled (contact us to help setup your account accordingly)
- Cannot itself be a sub-account (no nested sub-accounts)
Authentication:
- OAuth2 Bearer Token OR API Key
- Public API keys are NOT supported for sub-accounts endpoints
Base URL
All endpoints are prefixed with:
https://api.psrestful.com/extra/v2/subaccountsAuthentication Methods
Option 1: OAuth2 (Recommended for web applications)
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...Option 2: API Key (Recommended for server-to-server)
X-API-Key: your-api-key-hereAPI Endpoints
1. List Sub-Accounts
Retrieve all sub-accounts for your parent account.
Endpoint:
GET /extra/v2/subaccounts/Permission: Parent account members only
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
include_inactive | boolean | No | false | Include soft-deleted sub-accounts |
Example Request:
curl -X GET "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=false" \
-H "X-API-Key: your-api-key-here"Response: 200 OK
{
"count": 2,
"results": [
{
"id": 123,
"name": "ACME Corp - Client A",
"external_customer_id": "mkf_12345",
"organization_contact_name": "John Doe",
"organization_contact_email": "john@acme.com",
"account_notes": "Large distributor in TX",
"is_active": true,
"api_keys_count": 2,
"created_at": "2025-01-15T10:30:00Z",
"modified_on": "2025-01-20T14:45:00Z",
"created_by": "admin@yourplatform.com"
}
]
}Response Fields:
| Field | Type | Description |
|---|---|---|
count | integer | Total number of sub-accounts |
results | array | List of sub-account objects |
id | integer | Unique sub-account identifier |
name | string | Sub-account display name |
external_customer_id | string/null | Your internal customer/client ID |
organization_contact_name | string/null | Contact person name |
organization_contact_email | string/null | Contact person email |
account_notes | string/null | Internal notes |
is_active | boolean | Active status (false = soft deleted) |
api_keys_count | integer | Number of API keys for this sub-account |
created_at | datetime | ISO 8601 timestamp |
modified_on | datetime | ISO 8601 timestamp |
created_by | string/null | Email of creator or “API Key” |
Errors:
403 Forbidden- Not authenticated or not a parent account member
2. Create Sub-Account
Create a new sub-account under your parent account.
Endpoint:
POST /extra/v2/subaccounts/Permission: Parent account members only
Request Body:
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
name | string | Yes | 255 | Sub-account display name |
external_customer_id | string | Yes | 255 | Your internal customer ID (must be unique) |
organization_contact_name | string | No | 255 | Contact person name |
organization_contact_email | string | No | - | Valid email address |
account_notes | string | No | - | Internal notes |
Example Request:
curl -X POST "https://api.psrestful.com/extra/v2/subaccounts/" \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
"organization_contact_email": "admin@acme.com",
"account_notes": "Enterprise customer - high volume"
}'Response: 201 Created
{
"id": 456,
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
"organization_contact_name": null,
"organization_contact_email": "admin@acme.com",
"account_notes": "Enterprise customer - high volume",
"is_active": true,
"parent_account": null,
"created_at": "2025-11-23T10:30:00Z",
"modified_on": "2025-11-23T10:30:00Z",
"created_by": "admin@yourplatform.com",
"modified_by": "admin@yourplatform.com"
}What’s Inherited from Parent:
selected_plan- Subscription plan level
Automatic Settings:
allow_sub_accounts=false(sub-accounts cannot create their own sub-accounts)is_active=true
Errors:
403 Forbidden- Parent account doesn’t have permission to create sub-accounts400 Bad Request- Duplicateexternal_customer_idwithin your sub-accounts400 Bad Request- Validation errors (missing required fields, invalid format)
3. Get Sub-Account Details
Retrieve details for a specific sub-account.
Endpoint:
GET /extra/v2/subaccounts/{sub_account_id}/Permission: Parent account members OR sub-account members
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sub_account_id | integer | Yes | Sub-account ID |
Example Request:
curl -X GET "https://api.psrestful.com/extra/v2/subaccounts/456/" \
-H "X-API-Key: your-api-key-here"Response: 200 OK
{
"id": 456,
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
"organization_contact_name": null,
"organization_contact_email": "admin@acme.com",
"account_notes": "Enterprise customer - high volume",
"is_active": true,
"parent_account": {
"id": 1,
"name": "Your Platform Account"
},
"created_at": "2025-11-23T10:30:00Z",
"modified_on": "2025-11-23T10:30:00Z",
"created_by": "admin@yourplatform.com",
"modified_by": "admin@yourplatform.com"
}Errors:
403 Forbidden- You don’t have access to this sub-account404 Not Found- Sub-account not found
4. Update Sub-Account
Update metadata for an existing sub-account.
Endpoint:
PATCH /extra/v2/subaccounts/{sub_account_id}/Permission: Parent account members only (sub-account members cannot update)
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sub_account_id | integer | Yes | Sub-account ID |
Request Body: (all fields optional)
| Field | Type | Max Length | Description |
|---|---|---|---|
name | string | 255 | Sub-account display name |
external_customer_id | string | 255 | Your internal customer ID |
organization_contact_name | string | 255 | Contact person name |
organization_contact_email | string | - | Valid email address |
account_notes | string | - | Internal notes |
Example Request:
curl -X PATCH "https://api.psrestful.com/extra/v2/subaccounts/456/" \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"name": "ACME Distributors (Updated)",
"account_notes": "Now our largest customer"
}'Response: 200 OK
Returns the updated sub-account object (same structure as create response).
Important:
- Only metadata can be updated (not plan, parent relationship, or is_active)
modified_ontimestamp is automatically updatedmodified_byfield is automatically set to current user or “API Key”
Errors:
403 Forbidden- Only parent account members can update sub-accounts404 Not Found- Sub-account not found400 Bad Request- Duplicateexternal_customer_id
5. Delete Sub-Account
Soft delete a sub-account (sets is_active=false).
Endpoint:
DELETE /extra/v2/subaccounts/{sub_account_id}/Permission: Parent account members only
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sub_account_id | integer | Yes | Sub-account ID |
Example Request:
curl -X DELETE "https://api.psrestful.com/extra/v2/subaccounts/456/" \
-H "X-API-Key: your-api-key-here"Response: 204 No Content
Important:
- This is a soft delete - data is retained
- Sub-account
is_activeis set tofalse - API keys remain in database but should not be used
- Can be retrieved with
include_inactive=truequery parameter - Cannot be undone via API (contact support to restore)
Errors:
403 Forbidden- Only parent account members can delete sub-accounts404 Not Found- Sub-account not found
6. List Sub-Account API Keys
Retrieve all API keys for a sub-account.
Endpoint:
GET /extra/v2/subaccounts/{sub_account_id}/api-keys/Permission: Parent account members OR sub-account members
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sub_account_id | integer | Yes | Sub-account ID |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
include_parent_managed | boolean | No | true | Include keys created by parent account |
Example Request:
curl -X GET "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/" \
-H "X-API-Key: your-api-key-here"Response: 200 OK
{
"count": 2,
"results": [
{
"key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"active": true,
"public": false,
"managed_by_parent": true,
"created_at": "2025-11-23T10:30:00Z",
"created_by": "admin@yourplatform.com",
"age_in_days": 7
}
]
}Response Fields:
| Field | Type | Description |
|---|---|---|
key | string | 32-character hexadecimal API key |
active | boolean | Whether key is active |
public | boolean | Whether this is a public API key |
managed_by_parent | boolean | True if created by parent account |
created_at | datetime | ISO 8601 timestamp |
created_by | string/null | Email of creator or “API Key” |
age_in_days | integer | Days since creation |
Errors:
403 Forbidden- You don’t have access to this sub-account404 Not Found- Sub-account not found
7. Create API Key for Sub-Account
Generate a new API key for a sub-account.
Endpoint:
POST /extra/v2/subaccounts/{sub_account_id}/api-keys/Permission: Parent account members OR sub-account members
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sub_account_id | integer | Yes | Sub-account ID |
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
public | boolean | No | false | Whether this is a public API key |
Example Request:
curl -X POST "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/" \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"public": false
}'Response: 201 Created
{
"key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"active": true,
"public": false,
"managed_by_parent": true,
"created_at": "2025-11-23T10:30:00Z",
"created_by": "admin@yourplatform.com",
"age_in_days": 0
}Important:
- API keys are 32-character hexadecimal strings (cryptographically secure)
- Keys are shown in plaintext only at creation - store them securely
managed_by_parentis automatically set:trueif created by parent account memberfalseif created by sub-account member
- This flag determines deletion permissions (see endpoint 8)
Errors:
403 Forbidden- You don’t have access to this sub-account404 Not Found- Sub-account not found
8. Delete API Key
Delete an API key for a sub-account.
Endpoint:
DELETE /extra/v2/subaccounts/{sub_account_id}/api-keys/{api_key}/Permission: Depends on managed_by_parent flag (see below)
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sub_account_id | integer | Yes | Sub-account ID |
api_key | string | Yes | The API key to delete (32-char hex string) |
Example Request:
curl -X DELETE "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "X-API-Key: your-api-key-here"Response: 204 No Content
Permission Rules:
| API Key Type | Who Can Delete |
|---|---|
managed_by_parent=true | Only parent account members |
managed_by_parent=false | Parent account members OR sub-account members |
Important:
- This is a hard delete (permanently removes the key)
- Deleted keys cannot be recovered
- Applications using the deleted key will receive
403 Forbiddenerrors
Errors:
403 Forbidden- You don’t have permission to delete this API key404 Not Found- API key not found
Permissions & Access Control
Permission Matrix
| Action | Parent Account Members | Sub-Account Members |
|---|---|---|
| List sub-accounts | ✅ Yes (all) | ❌ No |
| Create sub-account | ✅ Yes | ❌ No |
| View sub-account | ✅ Yes | ✅ Yes (own only) |
| Update sub-account | ✅ Yes | ❌ No |
| Delete sub-account | ✅ Yes | ❌ No |
| List API keys | ✅ Yes | ✅ Yes (own only) |
| Create API key | ✅ Yes (managed_by_parent=true) | ✅ Yes (managed_by_parent=false) |
| Delete parent-managed key | ✅ Yes | ❌ No |
| Delete non-parent-managed key | ✅ Yes | ✅ Yes |
Access Control Rules
Parent Account Members:
- Full access to all sub-accounts
- Can create, read, update, delete sub-accounts
- Can manage all API keys (including parent-managed)
Sub-Account Members:
- Access only to their own sub-account
- Cannot modify sub-account metadata
- Can create their own API keys (
managed_by_parent=false) - Cannot delete parent-managed API keys
Business Rules
Sub-Account Creation Requirements
Your parent account must meet these requirements:
-
Enterprise Plan
-
Feature Enabled
allow_sub_accounts = true- Contact support to enable this feature
-
Not a Sub-Account
- Parent account cannot itself be a sub-account
- No nested sub-accounts allowed
Validation Rules
-
Unique External Customer ID
external_customer_idmust be unique within your sub-accounts- Different parent accounts can reuse the same ID
- Use this to map to your internal customer/client IDs
-
Whitespace Trimming
- Leading/trailing whitespace is automatically removed from:
nameexternal_customer_id
- Leading/trailing whitespace is automatically removed from:
-
Email Validation
organization_contact_emailmust be a valid email format
Inheritance Rules
Sub-accounts automatically inherit these fields from the parent:
| Field | Description |
|---|---|
selected_plan | Subscription plan level (Free, Standard, Premium, Enterprise) |
Sub-accounts cannot have:
| Field | Value | Reason |
|---|---|---|
allow_sub_accounts | Always false | No nested sub-accounts |
Rate Limiting
- Sub-accounts inherit the parent’s
selected_plan - Rate limits apply to each sub-account independently
- Parent usage does NOT count against sub-account limits
- Sub-account usage does NOT count against parent limits
Complete Integration Example
Scenario: Marketfuel Creates Sub-Account for Client
Step 1: Authenticate with Parent Account
# Store your parent account API key securely
PARENT_API_KEY="your-parent-account-api-key"Step 2: Create Sub-Account
curl -X POST "https://api.psrestful.com/extra/v2/subaccounts/" \
-H "X-API-Key: $PARENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
"organization_contact_email": "admin@acme.com",
"account_notes": "Created via Marketfuel integration"
}'Response:
{
"id": 456,
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
...
}Step 3: Generate API Key for Sub-Account
# Save the sub_account_id from step 2
SUB_ACCOUNT_ID=456
curl -X POST "https://api.psrestful.com/extra/v2/subaccounts/$SUB_ACCOUNT_ID/api-keys/" \
-H "X-API-Key: $PARENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"public": false
}'Response:
{
"key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"active": true,
"public": false,
"managed_by_parent": true,
...
}Step 4: Store Mapping in Your Database
# Example: Store in your platform's database
marketfuel_clients = {
"mkf_client_789": {
"name": "ACME Distributors",
"psrestful_sub_account_id": 456,
"psrestful_api_key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}
}Step 5: Use Sub-Account API Key for PromoStandards Requests
# When ACME makes a request through Marketfuel,
# use their sub-account API key
SUB_ACCOUNT_API_KEY="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
curl -X GET "https://api.psrestful.com/v2/products?supplier=pcna&productId=1234" \
-H "X-API-Key: $SUB_ACCOUNT_API_KEY"Result:
- Request is authenticated as sub-account (id=456)
- Usage is tracked under ACME’s sub-account
- Rate limits apply to ACME’s sub-account independently
Error Handling
Error Response Format
All errors return JSON with a detail field:
{
"detail": "Error message describing what went wrong"
}HTTP Status Codes
| Code | Meaning | Common Causes |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
204 | No Content | Deletion succeeded (no response body) |
400 | Bad Request | Validation error, duplicate external_customer_id |
401 | Unauthorized | Invalid or missing authentication token |
403 | Forbidden | Authenticated but insufficient permissions |
404 | Not Found | Sub-account or API key not found |
429 | Too Many Requests | Rate limit exceeded |
Common Errors and Solutions
Error: “Parent account doesn’t have permission to create sub-accounts”
HTTP Status: 403 Forbidden
Causes:
- Parent account doesn’t have
allow_sub_accounts=true - Parent account is not Premium/Enterprise plan
- Parent account is itself a sub-account
Solution:
- Upgrade to Premium or Enterprise plan
- Contact support to enable sub-accounts feature
- Ensure you’re using a parent account, not a sub-account
Error: “Sub-account with external_customer_id ‘xxx’ already exists”
HTTP Status: 400 Bad Request
Cause:
- Another sub-account under the same parent already uses this
external_customer_id
Solution:
- Use unique IDs for each sub-account
- Check existing sub-accounts:
GET /extra/v2/subaccounts/ - Update the ID or use a different identifier
Error: “You don’t have access to this sub-account”
HTTP Status: 403 Forbidden
Causes:
- Using wrong API key (not parent or sub-account)
- User is not a member of sub-account or parent account
Solution:
- Verify you’re using the correct API key
- Ensure API key belongs to parent account or the specific sub-account
- Check sub-account ID is correct
Error: “Only parent account members can update sub-accounts”
HTTP Status: 403 Forbidden
Cause:
- Attempting to update using sub-account credentials
Solution:
- Use parent account API key or OAuth token
- Sub-account members can only view, not modify
Error: “Only parent account can delete parent-managed API keys”
HTTP Status: 403 Forbidden
Cause:
- Attempting to delete a
managed_by_parent=truekey without parent permissions
Solution:
- Use parent account credentials to delete parent-managed keys
- Sub-account members can only delete keys they created (
managed_by_parent=false)
Best Practices
1. Managing External Customer IDs
Use Your Internal IDs:
{
"external_customer_id": "your_platform_customer_id_123"
}Benefits:
- Easy mapping between your customers and sub-accounts
- Quick lookups in your database
- Consistent with your existing system
Example:
# Map your customer ID to sub-account
def get_psrestful_api_key(customer_id):
mapping = database.query(
"SELECT psrestful_api_key FROM customer_integrations "
"WHERE customer_id = %s", customer_id
)
return mapping['psrestful_api_key']2. API Key Security
Store Securely:
- Never commit API keys to version control
- Use environment variables or secure vaults (AWS Secrets Manager, HashiCorp Vault)
- Encrypt keys in your database
Rotate Regularly:
# Example: Rotate API key
def rotate_api_key(sub_account_id, old_key):
# Create new key
new_key = create_api_key(sub_account_id)
# Update your systems to use new key
update_customer_api_key(customer_id, new_key)
# Delete old key
delete_api_key(sub_account_id, old_key)Use HTTPS:
- Always use HTTPS for API calls
- Never send API keys over unencrypted connections
3. Handling Soft-Deleted Sub-Accounts
Check Active Status:
# Before using cached API key
sub_account = get_sub_account(sub_account_id)
if not sub_account['is_active']:
raise Exception("Sub-account has been deleted")Query with Include Inactive:
# Retrieve all sub-accounts including deleted
curl "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=true" \
-H "X-API-Key: your-api-key"4. Error Handling in Production
Implement Retry Logic:
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_session():
session = requests.Session()
retry = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('https://', adapter)
return session
# Use session for API calls
session = create_session()
response = session.get(
"https://api.psrestful.com/extra/v2/subaccounts/",
headers={"X-API-Key": api_key}
)5. Monitoring and Logging
Track API Usage:
import logging
logger = logging.getLogger(__name__)
def track_api_call(customer_id, endpoint, status_code):
logger.info(
f"API call: customer={customer_id}, "
f"endpoint={endpoint}, status={status_code}"
)Monitor Rate Limits:
- Track 429 responses
- Alert when approaching limits
- Consider implementing client-side rate limiting
Frequently Asked Questions
Can sub-accounts create their own sub-accounts?
No. Sub-accounts always have allow_sub_accounts=false. Only parent accounts can create sub-accounts.
Can I change which account is the parent?
No. The parent-child relationship cannot be changed after creation. You would need to create a new sub-account under a different parent.
What happens to API keys when I delete a sub-account?
API keys remain in the database but should not be used. The sub-account is soft-deleted (is_active=false), and authentication with its API keys should be treated as invalid in your application.
Can I restore a deleted sub-account?
Deleted sub-accounts cannot be restored via API. Contact support if you need to restore a deleted sub-account.
How do I know if an API key was created by the parent or sub-account?
Check the managed_by_parent field when listing API keys. true means parent created it, false means sub-account created it.
Do sub-accounts have their own billing?
No. All usage across sub-accounts rolls up to the parent account for billing purposes.
Can I transfer ownership of a sub-account to another parent?
No. Sub-accounts cannot be transferred between parents. Create a new sub-account under the new parent.
Support
For additional help:
- Email: devs@psrestful.com
Last Updated: November 2025 API Version: v2