# PSRESTful API Documentation — Full Content
> This file contains the complete text of all PSRESTful documentation pages.
> PSRESTful is a RESTful JSON proxy layer on top of PromoStandards, the promotional products industry's open standards.
> For a navigation index, see: https://docs.psrestful.com/llms.txt
> API Reference: https://api.psrestful.com/docs
> Website: https://psrestful.com
---
## PSRESTful API Documentation
Source: https://docs.psrestful.com
# The PSRESTful API Documentation 👋
Welcome to the PSRESTful API Documentation, we're glad you're here. Here you can find
everything you need to know about our API.
## Get Started 🚀
#### Basic Diagram

To help you get started, we've put together a list of resources:
1. **API Reference:** Familiarize yourself with our [API Reference](https://api.psrestful.com/docs)
and learn how to make the most out of our platform.
2. **Guides:** Read our [guides](/guides) in order to better understand our API and how to use it.
3. **Standards:** Get insights from the original [standards](/standards).
4. **Integrated Suppliers:** Here is the list of [Suppliers](https://psrestful.com/integrated-suppliers/) that are already integrated with us.
5. **PostMan Collection:** You can find our PostMan Collection [here](https://documenter.getpostman.com/view/26459586/2s9YC4TXhA).
---
## Why PSRESTful?
Source: https://docs.psrestful.com/why-ps-restful
**Ready to explore?** Browse the [Interactive API Reference](https://api.psrestful.com/docs) or see the full list of [Integrated Suppliers](/extra-apis#suppliers-api).
## The Problem with Direct PromoStandards Integration
PromoStandards has driven tremendous adoption across the promotional products industry, but integrating directly with supplier SOAP endpoints comes with real challenges:
- **SOAP/XML complexity** — Every request requires building XML envelopes, parsing WSDL files, and handling namespaces. Modern web and mobile applications expect JSON.
- **Supplier implementation quirks** — Each supplier interprets the specification slightly differently, so code that works with one supplier may break with another.
- **Inconsistent Part IDs across services** — The same product variant can have different Part IDs in the Media, Inventory, and PPC services, making it difficult to correlate data.
- **No caching layer** — Fetching full product catalogs from suppliers is slow. Without caching, catalog imports can take hours instead of minutes.
- **No image processing or product search** — PromoStandards provides raw media URLs but no tools for background removal, decoration mockups, color analysis, or full-text product search.
PSRESTful builds on PromoStandards — not against it. Every supplier endpoint still follows the PromoStandards specification under the hood. PSRESTful simply removes the friction.
## One API for 500+ Suppliers
Instead of maintaining separate SOAP integrations for each supplier, you make a single REST call to PSRESTful and get back clean JSON.

PSRESTful supports all major PromoStandards services:
| Service | What It Does | Guide |
|---------|-------------|-------|
| **Product Data** | Sellable products, product details, closeout items | [Product Data Guide](/guides/product-data) |
| **Media Content** | Product images, videos, and documents | [Media Content Guide](/guides/media-content) |
| **PPC** | Pricing, configuration, decoration charges, FOB points | [PPC Guide](/guides/ppc) |
| **Inventory** | Real-time stock levels by warehouse | [Inventory Guide](/guides/inventory) |
| **Purchase Orders** | Submit and manage purchase orders | [PO Guide](/guides/purchase-order) |
| **Order Status** | Track order status and history | [Order Status Guide](/guides/order-status) |
| **OSN** | Shipment tracking notifications | [OSN Guide](/guides/order-shipment-notification) |
| **Invoice** | Retrieve invoice data for orders | [Invoice Guide](/guides/invoice) |
Every response is clean JSON. No XML parsing. No WSDL files.
## Beyond PromoStandards
PSRESTful's Extra APIs provide enriched data and functionality that goes beyond the PromoStandards specification. See the full [Extra APIs reference](/extra-apis).
- **Products API** — Full-text product search, detailed product data with expandable pricing, inventory, and media in a single call
- **Suppliers API** — Supplier directory with Shopify-readiness flags, credential availability, and decoration support metadata
- **Brands API** — Brand listings organized by supplier
- **Categories API** — Product categorization for filtering and organization
- **Decorations API** — Decoration methods and pricing by supplier, including setup fees and location options
- **Part ID Mappings** — Cross-reference Part IDs across Media, Inventory, and PPC services to resolve the inconsistency problem
- **Web Scraper** — Extract packaging dimensions, case pack quantities, and weight data from supplier websites
- **Product Classifications** — Pre-mapped product types for Shopify, Google Shopping, Amazon, Avalara, and TaxJar
## Image Processing with PSMEDx
PSMEDx (PromoStandards Media Extended) is a suite of image processing APIs purpose-built for promotional products. No third-party image service handles the specific needs of this industry — virtual product mockups, Pantone color matching, decoration placement detection — so PSRESTful provides them natively.
- **Background Removal** — Remove backgrounds from product images for clean e-commerce listings
- **Product Decoration** — Apply customer artwork onto blank product images to generate virtual mockups
- **Vectorization** — Convert raster logos to SVG vector format for screen printing and embroidery
- **Color Analysis and Pantone Matching** — Extract dominant colors from images and find the nearest Pantone match
- **Image Classification** — Determine whether a product image is blank or decorated, single-part or group shot
- **Bounding Box Detection** — Automatically find decoration areas on product images for consistent artwork placement
See the full [PSMEDx API reference](/psmedx-api).
## Built for Performance
### Intelligent Caching
PSRESTful caches responses based on how frequently each data type changes:
- **Product data** — 1 day to 1 week depending on the endpoint
- **Media content** — 1 week for individual products, 1 day for modified-since queries
- **PPC (pricing)** — 1 week for configuration data
- **Inventory** — 4 hours (balances freshness with performance)
- **Transactional services** — Never cached (orders, shipments, invoices always go direct)
Need fresh data? Add a `Cache-Control: no-cache` header to bypass the cache for any request. See [Caching](/caching) for details.
### Compression
PSRESTful supports Zstandard, Brotli, and gzip compression. For large payloads like product catalogs and pricing configurations, compression reduces bandwidth by 50-90%. Send `Accept-Encoding: zstd, br, gzip` and the server selects the best option automatically. See [Compression](/compression).
### Environments
Switch between production and sandbox environments with a single query parameter — `environment=PROD` or `environment=STAGING`. No separate base URLs or configurations needed. See [Environments](/environments).
## Security and Access Control
### Authentication Methods
| Method | Use Case |
|--------|----------|
| **Private API Keys** | Server-to-server integration (most common) |
| **Public API Keys** | Frontend applications with CORS domain allowlisting |
| **OAuth2 Bearer Tokens** | Web applications and transactional operations (required for order submission) |
| **Basic Auth** | Legacy compatibility |
See [Authentication](/authentication) for setup instructions.
### Role-Based Access Control
PSRESTful provides 5 roles with granular dashboard permissions — Admin, Developer, Operations, Product Analyst, and Customer Support. Each role controls access to API keys, data explorer, orders, product analytics, and account settings. See [Roles & Permissions](/roles).
### Credential Security
Supplier credentials managed through the [Credentials API](/credentials-api) are encrypted at rest and never exposed in API responses. Each sub-account can have independent supplier credentials with full audit trail support.
## Built for SaaS Platforms
If you run a SaaS platform that provides promotional products capabilities to your customers, PSRESTful's Sub-Accounts model lets you manage everything from a single parent account:
- **Create sub-accounts** for each of your customers programmatically
- **Generate per-sub-account API keys** with independent rate limits
- **Map sub-accounts** to your internal customer IDs via `external_customer_id`
- **Configure per-customer supplier credentials** for customer-specific pricing and transactional access
- **Track usage centrally** while maintaining isolation between customers
Sub-Accounts require an Enterprise plan. See [Sub-Accounts API](/sub-accounts-api) and [Credentials API](/credentials-api).
## Developer Experience
- **Interactive API Reference** — Full Swagger/OpenAPI documentation at [api.psrestful.com/docs](https://api.psrestful.com/docs)
- **Postman Collection** — Ready-to-import collection for quick testing
- **Code Examples** — Python, JavaScript, and cURL examples on every guide page
- **20+ How-To Guides** — Step-by-step guides for every PromoStandards service and common workflows
- **Rate Limit Headers** — Every response includes `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you always know your usage
## Plans
| Plan | Daily Limit | Best For |
|------|------------|----------|
| **Free** | 10 calls/day | Evaluation and testing |
| **Standard** | 300 calls/day | Small integrations and development |
| **Premium** | Unlimited | Production applications |
| **Enterprise** | Unlimited + Sub-Accounts | SaaS platforms and multi-tenant applications |
See [Rate Limits](/rate-limits) for details on headers, burst behavior, and upgrading.
## Get Started
1. **Create an account** at [psrestful.com](https://psrestful.com)
2. **Get your API key** from the [dashboard](https://psrestful.com/dashboard/api-keys/)
3. **Explore the API** — try the [Interactive API Reference](https://api.psrestful.com/docs) or follow one of the [how-to guides](/guides)
4. **Need help?** Contact us at devs@psrestful.com
---
## Authentication
Source: https://docs.psrestful.com/authentication
# Authentication
Our API offers multiple authentication methods to suit different use cases. The various methods provide flexibility and robust security, ensuring that your interaction with our API aligns with the best practices in the industry.
#### 1. Private API KEY Authentication
API KEY authentication is a simple method that requires including your unique API key in the request header.
You can obtain this key from your user [dashboard](https://psrestful.com/dashboard/api-keys/).
```
x-api-key: YOUR_API_KEY_HERE
```
You will be able to create API keys for your account and manage them from your user dashboard. This method is safe, but if someone gets a hold of your API key, they will be able to access your account. A private API key can be used for order submission, but because it is a long-lived, static credential, we **recommend** using the `OAuth2` method for order submission and other account operations — its tokens are short-lived, rotatable, and scoped to a single app. See our [OAuth2 Integration Guide](/guides/oauth2-integration-guide).
Here is an example in python:
```python
}
response = requests.request("GET", url, headers=headers)
print(response.json())
```
### 2. Public API keys
## Overview
Public API Keys allow you to authenticate and authorize access to the API from your front-end applications, such as **websites**, **mobile apps**, or **backoffice** applications. This method involves a three-step process:
1. Generating a public key
2. Adding your domains to **Allowed Origins** in Account Settings
3. Using the public key to make API requests
## Generating a Public Key
The public key is a unique identifier that is used to authenticate and authorize access to the API. It is a 32-character string that is generated by the user and stored in the database.
To generate a public key, follow these steps:
1. Log in to your account.
2. Navigate to the [API KEY](https://psrestful.com/dashboard/api-keys/public/) section.
3. Click on "Create API Key".
4. Save the generated key in a secure place.
## Adding Allowed Origins
To ensure that your public key is only used by authorized domains, you need to add your domains to the **Allowed Origins** in Account Settings.
1. Log in to your account.
2. Go to [Account Settings](https://psrestful.com/dashboard/account-settings/).
3. Find the Allowed Origins section.
4. Add the domains from which you will be making API requests.
5. Save your settings.
## Using the Public Key
Once you have generated your public key and added your domains to **Allowed Origins**, you can use the public key to make API requests.
Include the public key in your query parameters as shown below:
ex. https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/5989?pub_api_key=XXXXXX
Note: This will not work unless you launch this request from a page hosted on any of the domains you entered in step 2.
#### 3. Bearer Token Authentication
Bearer token authentication provides secure access using a token that can be obtained through `OAuth2` or other token providers. This method aligns with the best practices in security:
```
Authorization: Bearer YOUR_TOKEN_HERE
```
##### 3.1 OAuth2
We strongly recommend using `OAuth2` tokens when available, as they adhere to an industry-standard protocol for authorization. `OAuth2` provides several grant types, allowing for fine-grained control over access and refreshing tokens. Consult our [OAuth2 Integration Guide](/guides/oauth2-integration-guide) for detailed information on implementing `OAuth2` with our API.
### Security Considerations
- **Transport Layer Security**: Ensure that you are connecting over HTTPS to maintain the confidentiality and integrity of your data.
- **Token Storage**: Be cautious with how you store and handle tokens, particularly in client-side applications. It's essential to follow secure practices for storing sensitive information.
- **Rate Limiting**: Be aware of the rate limits that apply to your chosen authentication method. Our documentation on [Rate Limits](/rate-limits) provides more details.
- **Permissions**: In our case, for now, a user with access will have access to all API calls but we will change that in the near future. We will provide more details on how to manage permissions as changes occur.
Please refer to the specific endpoint documentation to determine which authentication methods are supported and any additional requirements.
---
## Note on Environments
Source: https://docs.psrestful.com/environments
# Note on Environments
Even though several suppliers allow two different environments(`production` and `sandbox`) we use
the [same api](https://api.psrestful.com/docs) to handle both environments.
The values for the environments are:
- `PROD` for production
- `STAGING` for sandbox
In the case of `send_po`(purchase-orders) we need to specify the environment in the request body.
```json
{
"environment": "STAGING",
...
}
```
For the rest of the endpoints we use a param in the query. For example:
### Staging(sandbox)
```url copy
https://api.psrestful.com/v2.0.0/suppliers/PCNA/sellable-products?environment=STAGING
```
### Production
```url copy
https://api.psrestful.com/v2.0.0/suppliers/PCNA/sellable-products?environment=PROD
```
NOTE: The environment param is case sensitive. If not environment is providec `PROD` is the default value.
---
## Rate Limits
Source: https://docs.psrestful.com/rate-limits
# Rate Limits
Rate limits are applied to our API to ensure fair usage and prevent abuse. Different limits apply depending on the authentication method used and the plan selected by the user.
#### API KEY and Bearer Token Users
Rate limits for API KEY and bearer token users are determined by the sales plan chosen. The following limits apply:
- **Free Plan**: 10 calls per day.
- **Standard Plan**: 300 calls per day.
- **Premium Plan**: Unlimited calls per day.
#### Important Notes
- **Rate Limit Headers**: Each response from the API includes headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`) that provide information about the current rate limit status for your request.
- **Exceeding Rate Limits**: If you exceed the rate limits, a `429 Too Many Requests` status code will be returned. You must wait until the rate limit window resets to make additional calls.
- **Upgrading Plans**: If your needs exceed the rate limits of your current plan, you may consider upgrading to a higher plan through your user dashboard. The change will take effect immediately.
- **Burst Behavior**: Please note that rate limits are enforced at the specified intervals (e.g., per hour, per day). Any unused calls do not roll over to the next interval.
### Considerations for Implementation
- **Caching**: Consider caching results locally to minimize the number of calls and stay within the rate limits.
- **Monitoring**: Implement monitoring on your side to keep track of the number of calls made and prevent unexpected rate limit issues.
- **Selecting the Right Plan**: Choose the plan that best fits your needs, taking into consideration the rate limits and the nature of your application.
By adhering to these rate limits, you contribute to a fair and stable environment for all users of our API.
---
## Caching
Source: https://docs.psrestful.com/caching
# Caching
## Overview
PSRESTful implements on-demand caching to enhance API performance, particularly for catalog creation operations. Our caching approach follows the recommendations of the PromoStandards Best Practices Committee and aims to reduce server load while accelerating response times.
Caching is especially valuable when importing large product catalogs from major suppliers, substantially decreasing the time needed for full catalog imports.
## How It Works
When you make an API request, PSRESTful checks if a cached response exists for that specific request. If a valid cached response is found, it's returned immediately without contacting the supplier's server. If no cache exists or it has expired, PSRESTful fetches fresh data from the supplier, caches it, and returns the response.
## Cache Timeouts by Service
Different services have different cache durations based on how frequently the underlying data typically changes:
### Product Data Service
| Endpoint | Cache Duration |
|----------|----------------|
| Sellable products (`/sellable-products`) | 1 day |
| Individual products (`/products/{id}`) | 1 week |
| Closeout products (`/products-closeout`) | 1 day |
| Products modified since (`/products-modified-since`) | 1 day |
### Media Content Service
| Endpoint | Cache Duration |
|----------|----------------|
| Media content (`/medias/{product_id}`) | 1 week |
| Media modified since (`/media-modified-since`) | 1 day |
### Pricing and Configuration (PPC) Service
| Endpoint | Cache Duration |
|----------|----------------|
| Available locations | 1 week |
| Decoration colors | 1 week |
| FOB points | 1 week |
| Available charges | 1 week |
| Configuration and pricing | 1 week |
### Inventory Service
| Endpoint | Cache Duration |
|----------|----------------|
| Filter values | 1 day |
| Inventory levels | 4 hours |
### Transactional Services (No Caching)
The following services operate transactionally and are **not cached**:
- **Purchase Order Service** - Orders are always sent in real-time
- **Order Status Service** - Status is always fetched fresh
- **Invoice Service** - Invoices are always fetched fresh
- **Order Shipment Notification (OSN)** - Notifications are always fetched fresh
## Bypassing the Cache
If you need to retrieve fresh data and bypass the cache, add the `Cache-Control` header to your request:
```bash
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/" \
-H "X-API-Key: your-api-key" \
-H "Cache-Control: no-cache"
```
### When to Bypass Cache
- When you know a supplier has just updated their data
- When debugging data discrepancies
- When you need real-time inventory for order placement
**Note:** Bypassing cache increases response time and puts additional load on supplier servers. Use sparingly and only when necessary.
## Best Practices
1. **Leverage caching for catalog imports**: When building or updating your product catalog, the cache significantly speeds up the process.
2. **Use incremental sync endpoints**: Instead of fetching all data, use endpoints like `/products-modified-since` and `/media-modified-since` to only get changed data.
3. **Plan around cache durations**: Schedule your sync jobs knowing that inventory data is cached for 4 hours while product data is cached for up to a week.
4. **Bypass cache strategically**: Only use `Cache-Control: no-cache` when you specifically need fresh data, not as a default.
## Related Documentation
- [Rate Limits](/rate-limits) - API usage limits
- [Design Decisions](/design-decisions) - Why we built PSRESTful the way we did
---
## PromoStandards Core Services — Distributor Workflow Overview
Source: https://docs.psrestful.com/promo-standards-core-services-overview
# PromoStandards Core Services — Distributor Workflow Overview
PromoStandards is a set of XML/SOAP-based standards designed to standardize communication between **distributors** and **suppliers** in the promotional products industry. Each service focuses on a specific business capability, and together they form an end‑to‑end integration lifecycle.
---

## High-Level Distributor Workflow
From a distributor’s perspective, the lifecycle typically looks like this:
1. **Build and maintain a product catalog**
- Import product data, pricing, and media from suppliers
2. **Keep availability accurate**
- Frequently update inventory levels
3. **Place orders with suppliers**
- Submit purchase orders
4. **Track fulfillment**
- Receive order status updates
5. **Notify customers of shipment**
- Receive shipping confirmations and tracking numbers
Each PromoStandards service maps directly to one or more of these steps.
---
## 1. Product / Pricing / Media Import
**Services:**
- [Product Data (Product)](/standards/product-data-2.0.0)
- [Pricing & Configuration (PPC)](/standards/ppc-1.0.0)
- [Media Content (MED)](/standards/media-content-1.1.0)
### Main Goal
To allow distributors to **build and maintain a searchable, sellable product catalog** using standardized supplier data.
### What It Provides
- **[Product Data](/standards/product-data-2.0.0)**
- Product names, descriptions, categories
- Attributes like sizes, colors, materials
- Supplier SKUs and product identifiers
- **[Pricing & Configuration (PPC)](/standards/ppc-1.0.0)**
- Base pricing tiers
- Decoration methods and locations
- Upcharges, setup fees, and run charges
- Minimum quantities and quantity breaks
- **[Media Content](/standards/media-content-1.1.0)**
- Product images (blank and decorated)
- ArtTemplates, virtual samples, and supporting assets
- Image metadata (angle, color, resolution, part relationships)
### Distributor Value
- Creates a **single internal catalog** across many suppliers
- Enables search, merchandising, and quoting
- Reduces manual data entry and supplier-by-supplier differences
### Position in Workflow
> **Foundation layer** — everything else depends on accurate product, pricing, and media data.
---
## 2. Inventory Availability
**Service:**
- [Inventory Availability (INV)](/standards/inventory-2.0.0)
### Main Goal
To provide **near real-time stock availability** so distributors can sell with confidence.
### What It Provides
- Inventory levels by:
- SKU / part
- Color
- Size
- Warehouse / location
- Optional future availability or backorder indicators
### Distributor Value
- Prevents overselling
- Enables accurate delivery expectations
- Critical for ecommerce storefronts and automation
### Typical Usage Pattern
- Polled on a **schedule** (every few hours or daily depending on the supplier)
- Queried **just-in-time** during checkout or quoting
### Position in Workflow
> **Operational accuracy layer** — keeps the catalog sellable and trustworthy.
---
## 3. Purchase Order Submission
**Service:**
- [Purchase Order (PO)](/standards/purchase-order-1.0.0)
### Main Goal
To allow distributors to **electronically place orders with suppliers** in a standardized format.
### What It Provides
- Order header information
- Distributor info
- Supplier info
- Order dates and references
- Line items
- Products and quantities
- Decoration details
- Shipping information
- Ship-to addresses
- Shipping methods
- Artwork references or attachments (depending on implementation)
### Distributor Value
- Eliminates manual order entry (email, fax, portals)
- Enables straight-through processing
- Scales order volume without increasing staff
### Position in Workflow
> **Transaction execution layer** — converts a sale into a supplier order.
---
## 4. Order Status Updates
**Service:**
- [Order Status (ORDSTAT)](/standards/order-status-2.0.0)
### Main Goal
To provide **visibility into the lifecycle of an order after submission**.
### What It Provides
- Order-level and line-level status updates
- Common statuses include:
- Received
- In production
- Backordered
- Shipped
- Cancelled
- Estimated ship dates and revisions
### Distributor Value
- Reduces "where is my order?" inquiries
- Enables proactive customer communication
- Supports internal exception handling
### Typical Usage Pattern
- Polled on a schedule
- Sometimes triggered by supplier-side updates
### Position in Workflow
> **Fulfillment visibility layer** — tracks progress from order to shipment.
---
## 5. Order Shipment Notification
**Service:**
- [Order Shipment Notification (OSN)](/standards/order-shipment-notification-2.0.0)
### Main Goal
To notify distributors when **an order (or part of an order) has shipped**.
### What It Provides
- Shipment confirmation
- Carrier and service
- Tracking numbers
- Ship dates
- Partial shipment details (split shipments)
### Distributor Value
- Enables automated customer notifications
- Allows tracking links to be surfaced in portals
- Feeds accounting, invoicing, and CRM systems
### Relationship to Order Status
- [OSN](/standards/order-shipment-notification-2.0.0) is often the **final milestone** after [ORDSTAT](/standards/order-status-2.0.0)
- Some suppliers rely more heavily on OSN than detailed status polling
### Position in Workflow
> **Customer fulfillment layer** — closes the loop from supplier to end customer.
---
## End-to-End Workflow Summary
1. **Import Catalog**
- [Product Data](/standards/product-data-2.0.0) + [Pricing](/standards/ppc-1.0.0) + [Media](/standards/media-content-1.1.0) build the catalog
2. **Maintain Accuracy**
- [Inventory Availability](/standards/inventory-2.0.0) keeps listings trustworthy
3. **Execute Sale**
- [Purchase Order](/standards/purchase-order-1.0.0) submits the order to the supplier
4. **Monitor Progress**
- [Order Status](/standards/order-status-2.0.0) tracks production and changes
5. **Complete Fulfillment**
- [Order Shipment Notification](/standards/order-shipment-notification-2.0.0) confirms delivery
Together, these services form a **modular but complete integration contract** between distributors and suppliers.
---
## Key Insight
PromoStandards is not a single API — it is a **workflow language** for the promotional products industry.
Distributors who implement all services gain:
- Operational scale
- Fewer manual touchpoints
- Better customer experience
- Stronger supplier relationships
Suppliers who implement them well become:
- Easier to do business with
- More attractive to high-volume distributors
- Better positioned for automation-driven growth
---
## PromoStandards General Rollout Strategy for Distributors
Source: https://docs.psrestful.com/promostandards-general-rollout-strategy-for-distributors
# PromoStandards General Rollout Strategy for Distributors
#### Introduction
Implementing PromoStandards services is crucial for distributors aiming to reduce costs, improve efficiency, and boost profitability. PromoStandards offers a suite of standardized APIs that streamline communication with suppliers, reduce manual errors, and speed up product delivery. For distributors, adopting these services can lead to significant operational improvements and cost savings.
#### Importance of PromoStandards Services
Every company seeks to reduce costs, improve efficiency, and enhance profitability. Implementing PromoStandards in your organization is crucial for achieving these goals. The widely used PromoStandards services include:
- **Product Data (Product)**
- **Media Content (MED)**
- **Pricing and Configuration (PPC)**
- **Inventory (INV)**
- **Purchase Order (PO)**
- **Order Status (ODRSTAT)**
- **Order Shipment Notification (OSN)**
These services help businesses create a comprehensive and accurate product catalog, maintain up-to-date inventory, streamline order processes, and provide timely order status updates, all of which contribute to cost reduction and improved efficiency.
#### General Rollout Strategy
Most distributors start with simpler APIs and progressively work towards more complex ones. The integration paths can be categorized into two primary paths: Transactional and Non-Transactional.
##### Transactional Path:
1. **Inventory (INV)**: Start with checking inventory levels.
2. **Order Status (ODRSTAT)**: Integrate to track the status of orders.
3. **Order Shipment Notification (OSN)**: Set up notifications for order shipments.
##### Non-Transactional Path:
1. **Media Content (MED)**: Begin by integrating media content for products.
2. **Product Data (Product)**: Integrate product information for a detailed catalog.
Ultimately, both paths converge on the need for Pricing & Configuration (PPC) before implementing Purchase Orders (PO), as PPC is essential for accurate order submissions. Purchase Orders will then facilitate the actual order placements.
#### Integration Considerations
**Call Velocity**: Avoid excessive API calls which can overload supplier endpoints and lead to reduced performance. For example, querying order status every hour instead of every minute is sufficient and more efficient.
**Retry Logic**: Implement simple retry mechanisms for failed calls, especially for critical operations like submitting purchase orders.
**Caching Data Locally**: Cache non-transactional data (like product and media information) locally to reduce redundant API calls and improve response times.
**Audit Logs**: Maintain logs of requests and responses to track the success of integrations, troubleshoot issues, and report on service usage.
#### How PromoStandards Services Help Reduce Costs and Improve Efficiency
1. **Product Data (Product)**: Provides comprehensive product information, aiding in creating a detailed product catalog.
2. **Media Content (MED)**: Supplies images and videos for products, enhancing the visual appeal of the catalog.
3. **Pricing and Configuration (PPC)**: Offers detailed pricing and configuration options, ensuring accurate pricing and reducing the likelihood of errors in order submissions.
4. **Inventory (INV)**: Helps manage stock levels, preventing over-selling and stockouts.
5. **Purchase Order (PO)**: Automates order placements, reducing manual errors and speeding up the order processing time.
6. **Order Status (ODRSTAT)**: Enables automatic status updates, reducing the need for manual follow-ups.
7. **Order Shipment Notification (OSN)**: Provides timely shipment information, enhancing customer satisfaction and enabling better shipment tracking.
### Detailed Service Descriptions
**Product Data + Media Content**: Together, they create a basic yet comprehensive catalog, allowing distributors to display products with multiple images and videos.
**Pricing and Configuration (PPC)**: Provides control over pricing and product configuration, displaying available decoration locations and imprint methods. It also includes all charges, ensuring accurate pricing.
**Purchase Order (PO)**: Reduces back-and-forth communication with suppliers, leading to faster production and reduced costs.
**Order Status (ODRSTAT)**: Automates status checks, allowing quick resolution of holds and speeding up production times.
By strategically rolling out these services and leveraging their capabilities, distributors can significantly enhance their operational efficiency and cost-effectiveness.
---
## Credentials API
Source: https://docs.psrestful.com/credentials-api
|---------|--------------------------------|
| **Purchase Order (PO)** | Orders are placed under your supplier account |
| **Order Status (OSTAT)** | Only your orders are visible with your credentials |
| **Order Shipment Notification (OSN)** | Shipment data is tied to your orders |
| **Invoice** | Invoices are generated for your account |
**3. Inventory with Customer Allocation**
Some suppliers provide customer-specific inventory allocations. Using your credentials ensures you see your allocated inventory, not just general availability.
### Use Case: SaaS Platforms with Sub-Accounts
This feature is essential for **enterprise SaaS platforms** that manage multiple distributor clients through [Sub-Accounts](/sub-accounts-api). Here's a typical workflow:
```
┌─────────────────────────────────────────────────────────────────┐
│ SaaS Platform Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Your Platform (Parent Account) │
│ └── Sub-Account: Client A (Distributor) │
│ └── Credentials: Client A's supplier credentials │
│ • SanMar credentials → Client A's pricing & orders │
│ • HIT credentials → Client A's pricing & orders │
│ │
│ └── Sub-Account: Client B (Distributor) │
│ └── Credentials: Client B's supplier credentials │
│ • SanMar credentials → Client B's pricing & orders │
│ • PCNA credentials → Client B's pricing & orders │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Benefits for SaaS Platforms:**
- **Customer Isolation**: Each sub-account has its own credentials, ensuring customers only see their own pricing and orders
- **Seamless Integration**: Your platform makes API calls on behalf of customers using their stored credentials
- **Centralized Management**: Manage all customer credentials through a single API
- **No Credential Sharing**: Customers don't need to share credentials with your support team—they're securely stored and encrypted
### Key Benefits
- **Per-Account Credentials**: Store unique credentials for each supplier under your account
- **Service-Specific Credentials**: Configure different credentials for different PromoStandards services (Product, Inventory, PPC, etc.)
- **Environment Separation**: Maintain separate credentials for staging and production environments
- **Secure Storage**: All passwords are encrypted at rest and never exposed in API responses
- **Audit Trail**: Track who created or modified credentials and when
---
## Getting Started
### Prerequisites
- A PSRESTful account with API access
- Valid supplier credentials from your PromoStandards suppliers
### Base URL
All credentials endpoints are prefixed with:
```
https://api.psrestful.com/extra/v2/credentials
```
### Authentication
**Option 1: API Key (Recommended for server-to-server)**
```http
X-API-Key: your-api-key-here
```
**Option 2: OAuth2 Bearer Token**
```http
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
```
**Important:** Public API keys are NOT supported for credentials endpoints.
---
## API Endpoints
### 1. List All Credentials
Retrieve all supplier credentials configured for your account.
**Endpoint:**
```http
GET /extra/v2/credentials
```
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/credentials" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key-here",
},
});
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"count": 2,
"results": [
{
"supplier_code": "SanMar",
"supplier_name": "SanMar",
"has_staging_credentials": true,
"has_production_credentials": true,
"service_credentials": [
{
"service": "INV",
"environment": "PROD",
"username": "inv_user"
}
]
},
{
"supplier_code": "PCNA",
"supplier_name": "PCNA",
"has_staging_credentials": false,
"has_production_credentials": true,
"service_credentials": []
}
]
}
```
**Response Fields:**
| Field | Type | Description |
|-------|------|-------------|
| `count` | integer | Total number of configured suppliers |
| `results` | array | List of credential objects |
| `supplier_code` | string | Unique supplier identifier |
| `supplier_name` | string | Display name of the supplier |
| `has_staging_credentials` | boolean | Whether staging credentials are configured |
| `has_production_credentials` | boolean | Whether production credentials are configured |
| `service_credentials` | array | List of service-specific credentials |
**Errors:**
- `403 Forbidden` - Not authenticated or invalid API key
---
### 2. Create or Update Supplier Credentials
Create new credentials or update existing ones for a specific supplier.
**Endpoint:**
```http
POST /extra/v2/credentials
```
**Request Body:**
| Field | Type | Required | Max Length | Description |
|-------|------|----------|------------|-------------|
| `supplier_code` | string | Yes | 255 | Supplier code (e.g., SanMar, PCNA, HIT) |
| `staging_username` | string | No | 255 | Username for staging environment |
| `staging_password` | string | No | 255 | Password for staging environment |
| `production_username` | string | No | 255 | Username for production environment |
| `production_password` | string | No | 255 | Password for production environment |
**Example Request:**
```bash copy
curl -X POST "https://api.psrestful.com/extra/v2/credentials" \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"supplier_code": "SanMar",
"staging_username": "my_staging_user",
"staging_password": "my_staging_pass",
"production_username": "my_prod_user",
"production_password": "my_prod_pass"
}'
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
payload = {
"supplier_code": "SanMar",
"staging_username": "my_staging_user",
"staging_password": "my_staging_pass",
"production_username": "my_prod_user",
"production_password": "my_prod_pass",
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials";
const payload = {
supplier_code: "SanMar",
staging_username: "my_staging_user",
staging_password: "my_staging_pass",
production_username: "my_prod_user",
production_password: "my_prod_pass",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Response:** `201 Created`
```json
{
"supplier_code": "SanMar",
"supplier_name": "SanMar",
"has_staging_credentials": true,
"has_production_credentials": true,
"service_credentials": []
}
```
**Important:**
- Passwords are encrypted before storage
- Passwords are never returned in API responses
- If credentials already exist for the supplier, they will be updated
- You can provide only staging OR only production credentials
**Errors:**
- `403 Forbidden` - Not authenticated
- `404 Not Found` - Supplier code not found in our system
---
### 3. Get Supplier Credentials
Retrieve credentials for a specific supplier.
**Endpoint:**
```http
GET /extra/v2/credentials/{supplier_code}
```
**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `supplier_code` | string | Yes | Supplier code (e.g., SanMar) |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/credentials/HIT" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials/HIT";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key-here",
},
});
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"supplier_code": "HIT",
"supplier_name": "Hit Promotional Products",
"has_staging_credentials": true,
"has_production_credentials": true,
"service_credentials": [
{
"service": "INV",
"environment": "PROD",
"username": "inv_specific_user"
},
{
"service": "Product",
"environment": "PROD",
"username": "product_user"
}
]
}
```
**Errors:**
- `403 Forbidden` - Not authenticated
- `404 Not Found` - Credentials not found for this supplier
---
### 4. Delete Supplier Credentials
Delete all credentials for a specific supplier. This also deletes any service-specific credentials.
**Endpoint:**
```http
DELETE /extra/v2/credentials/{supplier_code}
```
**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `supplier_code` | string | Yes | Supplier code (e.g., HIT) |
**Example Request:**
```bash copy
curl -X DELETE "https://api.psrestful.com/extra/v2/credentials/HIT" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.delete(url, headers=headers)
print(response.status_code) # 204 No Content
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials/HIT";
const response = await fetch(url, {
method: "DELETE",
headers: {
"X-API-Key": "your-api-key-here",
},
});
console.log(response.ok); // true if 204 No Content
```
**Response:** `204 No Content`
**Important:**
- This is a **hard delete** - credentials cannot be recovered
- All service-specific credentials are also deleted (cascade delete)
- After deletion, PSRESTful will fall back to default credentials (if available)
**Errors:**
- `403 Forbidden` - Not authenticated
- `404 Not Found` - Credentials not found for this supplier
---
### 5. Create or Update Service-Specific Credentials
Configure credentials for a specific PromoStandards service. This is useful when a supplier requires different credentials for different services.
**Endpoint:**
```http
POST /extra/v2/credentials/{supplier_code}/services
```
**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `supplier_code` | string | Yes | Supplier code (e.g., HIT) |
**Request Body:**
| Field | Type | Required | Max Length | Description |
|-------|------|----------|------------|-------------|
| `service` | string | Yes | 10 | Service code (see table below) |
| `environment` | string | Yes | - | `STAGING` or `PROD` |
| `username` | string | Yes | 255 | Service-specific username |
| `password` | string | Yes | 255 | Service-specific password |
**Available Service Codes:**
| Service Code | Description |
|--------------|-------------|
| `Product` | Product Data Service |
| `MED` | Media Content Service |
| `INV` | Inventory Service |
| `PPC` | Product Pricing & Configuration |
| `PO` | Purchase Order Service |
| `OSN` | Order Shipment Notification |
| `OSTAT` | Order Status Service |
| `Invoice` | Invoice Service |
**Example Request:**
```bash copy
curl -X POST "https://api.psrestful.com/extra/v2/credentials/HIT/services" \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"service": "INV",
"environment": "PROD",
"username": "inventory_specific_user",
"password": "inventory_specific_pass"
}'
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
payload = {
"service": "INV",
"environment": "PROD",
"username": "inventory_specific_user",
"password": "inventory_specific_pass",
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials/HIT/services";
const payload = {
service: "INV",
environment: "PROD",
username: "inventory_specific_user",
password: "inventory_specific_pass",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Response:** `201 Created`
```json
{
"service": "INV",
"environment": "PROD",
"username": "inventory_specific_user"
}
```
**Important:**
- You must first create base supplier credentials before adding service-specific credentials
- Service-specific credentials override the base supplier credentials for that service/environment combination
- If the service credential already exists, it will be updated
**Errors:**
- `400 Bad Request` - Invalid environment (must be STAGING or PROD)
- `403 Forbidden` - Not authenticated
- `404 Not Found` - Supplier credentials not found (create base credentials first)
---
### 6. Delete Service-Specific Credentials
Delete credentials for a specific service/environment combination.
**Endpoint:**
```http
DELETE /extra/v2/credentials/{supplier_code}/services/{service}/{environment}
```
**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `supplier_code` | string | Yes | Supplier code (e.g., HIT) |
| `service` | string | Yes | Service code (e.g., INV) |
| `environment` | string | Yes | `STAGING` or `PROD` |
**Example Request:**
```bash copy
curl -X DELETE "https://api.psrestful.com/extra/v2/credentials/HIT/services/INV/PROD" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.delete(url, headers=headers)
print(response.status_code) # 204 No Content
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials/HIT/services/INV/PROD";
const response = await fetch(url, {
method: "DELETE",
headers: {
"X-API-Key": "your-api-key-here",
},
});
console.log(response.ok); // true if 204 No Content
```
**Response:** `204 No Content`
**Important:**
- Only deletes the specific service credential
- Base supplier credentials remain intact
- After deletion, the service will use the base supplier credentials
**Errors:**
- `403 Forbidden` - Not authenticated
- `404 Not Found` - Service credential not found
---
## Credential Resolution Hierarchy
When PSRESTful makes a PromoStandards API call, credentials are resolved in this order:
1. **Request-Level Credentials** - Credentials embedded in the API request (if provided)
2. **Service-Specific Credentials** - Credentials configured for the specific service and environment
3. **Account Supplier Credentials** - Base credentials configured for the supplier
4. **Default Credentials** - Shared credentials configured by PSRESTful (fallback)
This hierarchy allows for flexible credential management while ensuring API calls always have credentials available.
```
┌─────────────────────────────────────────────────────────────┐
│ Credential Resolution │
├─────────────────────────────────────────────────────────────┤
│ 1. Request-Level Credentials (Highest Priority) │
│ ↓ (if not found) │
│ 2. Service-Specific Credentials │
│ ↓ (if not found) │
│ 3. Account Supplier Credentials │
│ ↓ (if not found) │
│ 4. Default Credentials (Fallback) │
└─────────────────────────────────────────────────────────────┘
```
---
## Security Features
### Password Encryption
All passwords are encrypted before being stored in our database using industry-standard encryption algorithms. Passwords are never stored in plain text.
### Password Privacy
Passwords are **never** returned in API responses. The API only indicates whether credentials are configured through boolean flags (`has_staging_credentials`, `has_production_credentials`) or returns usernames for service credentials.
### Audit Trail
All credential operations are tracked with:
- `created_at` - Timestamp of creation
- `created_by` - User email who created the credentials
- `modified_on` - Timestamp of last modification
- `modified_by` - User email who last modified the credentials
### Cache Invalidation
When credentials are created, updated, or deleted, the credential cache is automatically invalidated to ensure the latest credentials are used for subsequent API calls.
---
## Complete Integration Example
### Scenario: Configure Supplier Credentials for Your Account
**Step 1: Create Base Supplier Credentials**
```bash copy
# Configure production credentials for Hit Promotional Products
curl -X POST "https://api.psrestful.com/extra/v2/credentials" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"supplier_code": "HIT",
"production_username": "your_prod_user",
"production_password": "your_prod_pass"
}'
```
```python copy
headers = {"X-API-Key": "your-api-key"}
payload = {
"supplier_code": "HIT",
"production_username": "your_prod_user",
"production_password": "your_prod_pass",
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials";
const payload = {
supplier_code: "HIT",
production_username: "your_prod_user",
production_password: "your_prod_pass",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Step 2: Add Service-Specific Credentials (Optional)**
```bash copy
# HIT uses different credentials for Inventory
curl -X POST "https://api.psrestful.com/extra/v2/credentials/HIT/services" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"service": "INV",
"environment": "PROD",
"username": "hit_inv_user",
"password": "hit_inv_pass"
}'
```
```python copy
headers = {"X-API-Key": "your-api-key"}
payload = {
"service": "INV",
"environment": "PROD",
"username": "hit_inv_user",
"password": "hit_inv_pass",
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials/HIT/services";
const payload = {
service: "INV",
environment: "PROD",
username: "hit_inv_user",
password: "hit_inv_pass",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Step 3: Verify Configuration**
```bash copy
# Check your configured credentials
curl -X GET "https://api.psrestful.com/extra/v2/credentials/HIT" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials/HIT";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
**Response:**
```json
{
"supplier_code": "HIT",
"supplier_name": "Hit Promotional Products",
"has_staging_credentials": false,
"has_production_credentials": true,
"service_credentials": [
{
"service": "INV",
"environment": "PROD",
"username": "hit_inv_user"
}
]
}
```
**Step 4: Make API Calls**
Now when you make PromoStandards API calls through PSRESTful, your configured credentials will be used automatically:
```bash copy
# This will use your configured Inventory credentials
curl -X GET "https://api.psrestful.com/v2/suppliers/HIT/inventory/PRODUCT123?environment=PROD" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
params = {"environment": "PROD"}
response = requests.get(url, headers=headers, params=params)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/v2/suppliers/HIT/inventory/PRODUCT123?environment=PROD";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
---
## Error Handling
### Error Response Format
All errors return JSON with a `detail` field:
```json
{
"detail": "Error message describing what went wrong"
}
```
### HTTP Status Codes
| Code | Meaning | Common Causes |
|------|---------|---------------|
| `200` | OK | Request succeeded |
| `201` | Created | Credentials created successfully |
| `204` | No Content | Deletion succeeded (no response body) |
| `400` | Bad Request | Invalid environment, validation error |
| `403` | Forbidden | Not authenticated or invalid API key |
| `404` | Not Found | Supplier not found or credentials not configured |
### Common Errors and Solutions
#### Error: "Supplier not found"
**HTTP Status:** `404 Not Found`
**Cause:** The supplier code provided does not exist in our system.
**Solution:**
- Verify the supplier code is correct
- Use the correct case (e.g., `SanMar`, `HIT`, `PCNA`)
- Check the [supported suppliers list](https://api.psrestful.com/docs)
---
#### Error: "Credentials not found"
**HTTP Status:** `404 Not Found`
**Cause:** No credentials have been configured for this supplier under your account.
**Solution:**
- Create base credentials first using `POST /extra/v2/credentials`
---
#### Error: "Parent credentials must exist first"
**HTTP Status:** `404 Not Found`
**Cause:** Attempting to create service-specific credentials without base supplier credentials.
**Solution:**
- Create base supplier credentials before adding service-specific credentials
---
## Best Practices
### 1. Use Service-Specific Credentials When Needed
Some suppliers require different credentials for different services. Check with your supplier if you're experiencing authentication issues with specific services.
### 2. Separate Staging and Production
Always configure separate credentials for staging and production environments when testing integrations.
### 3. Monitor Credential Usage
If API calls start failing with authentication errors, verify your credentials are still valid with the supplier.
### 4. Keep Credentials Updated
When you change passwords with a supplier, update them in PSRESTful immediately to avoid service disruptions.
### 5. Use Account-Level Credentials
For multi-tenant applications, use the Credentials API to manage per-customer supplier credentials, ensuring proper isolation and access control.
---
## Frequently Asked Questions
### Can I see the passwords I've configured?
No. For security reasons, passwords are never returned in API responses. The API only indicates whether credentials are configured.
### What happens if I don't configure credentials?
PSRESTful will fall back to default credentials if available. If no credentials are available, the API call will fail with a `403 Forbidden` error.
### Can I configure credentials for all suppliers at once?
No using the API. Credentials must be configured per-supplier using individual API calls. However, you can upload a CSV into our UI.
### Are credentials shared across sub-accounts?
No. Each account (including sub-accounts) has its own set of credentials. Sub-accounts inherit the parent's plan but not credentials.
### How long are credentials cached?
Credentials are cached for up to 24 hours for performance. The cache is automatically invalidated when you update or delete credentials.
---
## Related Documentation
- [Sub-Accounts API](/sub-accounts-api) - Create and manage sub-accounts for your customers
- [Authentication](/authentication) - Learn about API authentication methods
---
## Support
For additional help:
- **Email:** devs@psrestful.com
---
**Last Updated:** January 2025
**API Version:** v2
---
## Sub-Accounts API
Source: https://docs.psrestful.com/sub-accounts-api
- Each customer needs their own PSRESTful account
- Customers manage their own API keys
- No centralized control or visibility
- Complex onboarding process
**With Sub-Accounts:**
1. Your platform has one parent account (Enterprise plan)
2. Create a sub-account for each of your customers
3. Generate API keys for each sub-account
4. [Configure supplier credentials](/credentials-api) for each sub-account (for customer-specific pricing and transactional services)
5. Map sub-accounts to your customers via `external_customer_id`
6. Make PromoStandards API calls on behalf of your customers
7. 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/subaccounts
```
### Authentication Methods
**Option 1: OAuth2 (Recommended for web applications)**
```http
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
```
**Option 2: API Key (Recommended for server-to-server)**
```http
X-API-Key: your-api-key-here
```
---
## API Endpoints
### 1. List Sub-Accounts
Retrieve all sub-accounts for your parent account.
**Endpoint:**
```http
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:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=false" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=false";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key-here",
},
});
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"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:**
```http
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:**
```bash copy
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"
}'
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
payload = {
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
"organization_contact_email": "admin@acme.com",
"account_notes": "Enterprise customer - high volume",
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/";
const payload = {
name: "ACME Distributors",
external_customer_id: "mkf_client_789",
organization_contact_email: "admin@acme.com",
account_notes: "Enterprise customer - high volume",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Response:** `201 Created`
```json
{
"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-accounts
- `400 Bad Request` - Duplicate `external_customer_id` within your sub-accounts
- `400 Bad Request` - Validation errors (missing required fields, invalid format)
---
### 3. Get Sub-Account Details
Retrieve details for a specific sub-account.
**Endpoint:**
```http
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:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/subaccounts/456/" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/456/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key-here",
},
});
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"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-account
- `404 Not Found` - Sub-account not found
---
### 4. Update Sub-Account
Update metadata for an existing sub-account.
**Endpoint:**
```http
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:**
```bash copy
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"
}'
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
payload = {
"name": "ACME Distributors (Updated)",
"account_notes": "Now our largest customer",
}
response = requests.patch(url, json=payload, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/456/";
const payload = {
name: "ACME Distributors (Updated)",
account_notes: "Now our largest customer",
};
const response = await fetch(url, {
method: "PATCH",
headers: {
"X-API-Key": "your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**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_on` timestamp is automatically updated
- `modified_by` field is automatically set to current user or "API Key"
**Errors:**
- `403 Forbidden` - Only parent account members can update sub-accounts
- `404 Not Found` - Sub-account not found
- `400 Bad Request` - Duplicate `external_customer_id`
---
### 5. Delete Sub-Account
Soft delete a sub-account (sets `is_active=false`).
**Endpoint:**
```http
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:**
```bash copy
curl -X DELETE "https://api.psrestful.com/extra/v2/subaccounts/456/" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.delete(url, headers=headers)
print(response.status_code) # 204 No Content
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/456/";
const response = await fetch(url, {
method: "DELETE",
headers: {
"X-API-Key": "your-api-key-here",
},
});
console.log(response.ok); // true if successful (204 No Content)
```
**Response:** `204 No Content`
**Important:**
- This is a **soft delete** - data is retained
- Sub-account `is_active` is set to `false`
- API keys remain in database but should not be used
- Can be retrieved with `include_inactive=true` query parameter
- Cannot be undone via API (contact support to restore)
**Errors:**
- `403 Forbidden` - Only parent account members can delete sub-accounts
- `404 Not Found` - Sub-account not found
---
### 6. List Sub-Account API Keys
Retrieve all API keys for a sub-account.
**Endpoint:**
```http
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:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key-here",
},
});
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"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-account
- `404 Not Found` - Sub-account not found
---
### 7. Create API Key for Sub-Account
Generate a new API key for a sub-account.
**Endpoint:**
```http
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:**
```bash copy
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
}'
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
payload = {"public": False}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/";
const payload = { public: false };
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Response:** `201 Created`
```json
{
"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_parent` is automatically set:
- `true` if created by parent account member
- `false` if created by sub-account member
- This flag determines deletion permissions (see endpoint 8)
**Errors:**
- `403 Forbidden` - You don't have access to this sub-account
- `404 Not Found` - Sub-account not found
---
### 8. Delete API Key
Delete an API key for a sub-account.
**Endpoint:**
```http
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:**
```bash copy
curl -X DELETE "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "X-API-Key: your-api-key-here"
```
```python copy
headers = {"X-API-Key": "your-api-key-here"}
response = requests.delete(url, headers=headers)
print(response.status_code) # 204 No Content
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/456/api-keys/a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6";
const response = await fetch(url, {
method: "DELETE",
headers: {
"X-API-Key": "your-api-key-here",
},
});
console.log(response.ok); // true if successful (204 No Content)
```
**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 Forbidden` errors
**Errors:**
- `403 Forbidden` - You don't have permission to delete this API key
- `404 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:
1. **Enterprise Plan**
2. **Feature Enabled**
- `allow_sub_accounts = true`
- Contact support to enable this feature
3. **Not a Sub-Account**
- Parent account cannot itself be a sub-account
- No nested sub-accounts allowed
### Validation Rules
1. **Unique External Customer ID**
- `external_customer_id` must be unique **within your sub-accounts**
- Different parent accounts can reuse the same ID
- Use this to map to your internal customer/client IDs
2. **Whitespace Trimming**
- Leading/trailing whitespace is automatically removed from:
- `name`
- `external_customer_id`
3. **Email Validation**
- `organization_contact_email` must 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**
```bash
# Store your parent account API key securely
PARENT_API_KEY="your-parent-account-api-key"
```
**Step 2: Create Sub-Account**
```bash copy
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"
}'
```
```python copy
headers = {"X-API-Key": "your-parent-account-api-key"}
payload = {
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
"organization_contact_email": "admin@acme.com",
"account_notes": "Created via Marketfuel integration",
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/subaccounts/";
const payload = {
name: "ACME Distributors",
external_customer_id: "mkf_client_789",
organization_contact_email: "admin@acme.com",
account_notes: "Created via Marketfuel integration",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-parent-account-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Response:**
```json
{
"id": 456,
"name": "ACME Distributors",
"external_customer_id": "mkf_client_789",
...
}
```
**Step 3: Generate API Key for Sub-Account**
```bash copy
# 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
}'
```
```python copy
url = f"https://api.psrestful.com/extra/v2/subaccounts/{sub_account_id}/api-keys/"
headers = {"X-API-Key": "your-parent-account-api-key"}
payload = {"public": False}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
```
```javascript copy
const subAccountId = 456;
const url = `https://api.psrestful.com/extra/v2/subaccounts/${subAccountId}/api-keys/`;
const payload = { public: false };
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "your-parent-account-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
**Response:**
```json
{
"key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"active": true,
"public": false,
"managed_by_parent": true,
...
}
```
**Step 4: Store Mapping in Your Database**
```python copy
# Example: Store in your platform's database
marketfuel_clients = {
"mkf_client_789": {
"name": "ACME Distributors",
"psrestful_sub_account_id": 456,
"psrestful_api_key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}
}
```
```javascript copy
// Example: Store in your platform's database
const marketfuelClients = {
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**
```bash copy
# 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"
```
```python copy
# use their sub-account API key
url = "https://api.psrestful.com/v2/products?supplier=pcna&productId=1234"
headers = {"X-API-Key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
// When ACME makes a request through Marketfuel,
// use their sub-account API key
const url = "https://api.psrestful.com/v2/products?supplier=pcna&productId=1234";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
},
});
const data = await response.json();
console.log(data);
```
**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
**Step 6: Configure Supplier Credentials (Optional)**
To access customer-specific pricing or use transactional services (Purchase Orders, Order Status, etc.), configure supplier credentials for each sub-account using the [Credentials API](/credentials-api):
```bash copy
# Using the sub-account's API key, configure their supplier credentials
curl -X POST "https://api.psrestful.com/extra/v2/credentials" \
-H "X-API-Key: $SUB_ACCOUNT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"supplier_code": "SanMar",
"production_username": "acme_sanmar_user",
"production_password": "acme_sanmar_pass"
}'
```
```python copy
headers = {"X-API-Key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"}
payload = {
"supplier_code": "SanMar",
"production_username": "acme_sanmar_user",
"production_password": "acme_sanmar_pass",
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
```
```javascript copy
const url = "https://api.psrestful.com/extra/v2/credentials";
const payload = {
supplier_code: "SanMar",
production_username: "acme_sanmar_user",
production_password: "acme_sanmar_pass",
};
const response = await fetch(url, {
method: "POST",
headers: {
"X-API-Key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
```
Now ACME's API calls will use their own supplier credentials, giving them access to their negotiated pricing and order history.
---
## Error Handling
### Error Response Format
All errors return JSON with a `detail` field:
```json
{
"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=true` key 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:**
```json
{
"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:**
```python copy
# 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']
```
```javascript copy
// Map your customer ID to sub-account
async function getPsrestfulApiKey(customerId) {
const mapping = await database.query(
"SELECT psrestful_api_key FROM customer_integrations WHERE customer_id = $1",
[customerId]
);
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:**
```python copy
# 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)
```
```javascript copy
// Example: Rotate API key
async function rotateApiKey(subAccountId, oldKey) {
// Create new key
const newKey = await createApiKey(subAccountId);
// Update your systems to use new key
await updateCustomerApiKey(customerId, newKey);
// Delete old key
await deleteApiKey(subAccountId, oldKey);
}
```
**Use HTTPS:**
- Always use HTTPS for API calls
- Never send API keys over unencrypted connections
### 3. Handling Soft-Deleted Sub-Accounts
**Check Active Status:**
```python copy
# 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")
```
```javascript copy
// Before using cached API key
const subAccount = await getSubAccount(subAccountId);
if (!subAccount.is_active) {
throw new Error("Sub-account has been deleted");
}
```
**Query with Include Inactive:**
```bash copy
# Retrieve all sub-accounts including deleted
curl "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=true" \
-H "X-API-Key: your-api-key"
```
```python copy
url = "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=true"
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript copy
// Retrieve all sub-accounts including deleted
const url = "https://api.psrestful.com/extra/v2/subaccounts/?include_inactive=true";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### 4. Error Handling in Production
**Implement Retry Logic:**
```python copy
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}
)
```
```javascript copy
async function fetchWithRetry(url, options, retries = 3, backoff = 1000) {
const retryStatuses = [429, 500, 502, 503, 504];
for (let attempt = 0; attempt <= retries; attempt++) {
const response = await fetch(url, options);
if (!retryStatuses.includes(response.status) || attempt === retries) {
return response;
}
await new Promise((resolve) => setTimeout(resolve, backoff * (attempt + 1)));
}
}
// Use retry wrapper for API calls
const response = await fetchWithRetry(
"https://api.psrestful.com/extra/v2/subaccounts/",
{
method: "GET",
headers: { "X-API-Key": apiKey },
}
);
```
### 5. Monitoring and Logging
**Track API Usage:**
```python copy
```
```javascript copy
function trackApiCall(customerId, endpoint, statusCode) {
console.log(
`API call: customer=${customerId}, endpoint=${endpoint}, status=${statusCode}`
);
}
```
**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.
### How do I configure supplier credentials for sub-accounts?
Each sub-account can have its own supplier credentials configured via the [Credentials API](/credentials-api). This is essential for:
- Accessing customer-specific pricing through the PPC service
- Placing orders via the Purchase Order service
- Retrieving order status, shipment notifications, and invoices
Use the sub-account's API key when calling the Credentials API to configure credentials for that specific sub-account.
### Are credentials shared between parent and sub-accounts?
No. Each account (parent and sub-accounts) maintains its own set of supplier credentials. Sub-accounts inherit the parent's subscription plan but not credentials. See the [Credentials API documentation](/credentials-api) for details.
---
## Related Documentation
- [Credentials API](/credentials-api) - Manage supplier credentials for customer-specific pricing and transactional services
---
## Support
For additional help:
- **Email:** devs@psrestful.com
---
**Last Updated:** November 2025
**API Version:** v2
---
## Extra APIs
Source: https://docs.psrestful.com/extra-apis
- **Suppliers** - Supplier directory with capabilities and metadata
- **Brands** - Brand information organized by supplier
- **Categories** - Product categorization for organization and filtering
- **Inventory** - Cached inventory data with efficient querying
- **Decorations** - Decoration options and pricing by supplier
- **Part ID Mappings** - Cross-reference part IDs across different services
### API Versions
Extra APIs are available in two versions:
| Version | Base Path | Response Format | Status |
|---------|-----------|-----------------|--------|
| V1 | `/extra/v1` | snake_case | Stable |
| V2 | `/extra/v2` | camelCase | Recommended |
**Recommendation:** Use V2 endpoints for new integrations. V2 provides consistent pagination, camelCase responses, and additional features.
---
## Authentication
All Extra API endpoints require authentication.
**API Key:**
```http
X-API-Key: your-api-key-here
```
**Bearer Token:**
```http
Authorization: Bearer your-token-here
```
---
## Pagination
V2 endpoints use consistent pagination with the following parameters and response format:
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number (1-indexed) |
| `page_size` | integer | 20 | Items per page (max varies by endpoint) |
**Response Format:**
```json
{
"count": 150,
"page": 1,
"pageSize": 20,
"totalPages": 8,
"next": "https://api.psrestful.com/extra/v2/products?page=2",
"previous": null,
"results": [...]
}
```
---
## Products API
### List Products
Get a paginated list of products with optional filtering.
**V2 Endpoint:**
```http
GET /extra/v2/products
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
| `supplier_code` | string | Filter by supplier (e.g., SanMar, HIT) |
| `brand` | string | Filter by brand name |
| `category` | string | Filter by category |
| `search` | string | Search in product name/description |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products?supplier_code=SanMar&page_size=10" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products?supplier_code=SanMar&page_size=10",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 5420,
"page": 1,
"pageSize": 10,
"totalPages": 542,
"next": "https://api.psrestful.com/extra/v2/products?supplier_code=SanMar&page=2&page_size=10",
"previous": null,
"results": [
{
"extraId": 12345,
"productId": "PC61",
"supplierCode": "SanMar",
"name": "Port & Company Essential Tee",
"description": "A wardrobe essential...",
"imageUrl": "https://...",
"listPrice": 5.98,
"currency": "USD"
}
]
}
```
---
### Get Product Details
Get detailed product information with optional data expansions.
**V2 Endpoint:**
```http
GET /extra/v2/products/{product_id}
```
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `product_id` | string | Product ID or Extra ID |
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `expand` | string | Comma-separated expansions: `ppcs`, `inventory`, `media` |
| `shop` | string | Shopify shop domain (for shop-specific pricing) |
| `currency` | string | Currency code: `USD` or `CAD` |
| `country` | string | Country code for localized data |
| `language` | string | Language code |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products/PC61?expand=ppcs,inventory¤cy=USD" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products/PC61?expand=ppcs,inventory¤cy=USD",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"extraId": 12345,
"productId": "PC61",
"supplierCode": "SanMar",
"name": "Port & Company Essential Tee",
"description": "A wardrobe essential that icons are made of...",
"brand": "Port & Company",
"categories": ["Apparel", "T-Shirts"],
"imageUrl": "https://...",
"images": [...],
"colors": [...],
"sizes": [...],
"parts": [...],
"combinedPpc": {...},
"inventory": [...]
}
```
---
### Get Product PPCs (Pricing)
Get combined pricing data including list price, net price, and customer-specific pricing.
**V2 Endpoint:**
```http
GET /extra/v2/products/{product_id}/ppcs
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `currency` | string | USD | Currency: `USD` or `CAD` |
| `fob_point_id` | string | - | Specific FOB point for shipping calculations |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products/PC61/ppcs?currency=USD" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products/PC61/ppcs?currency=USD",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"productId": "PC61",
"currency": "USD",
"fobPoints": [...],
"priceBreaks": [
{
"minQuantity": 1,
"maxQuantity": 11,
"listPrice": 5.98,
"netPrice": 4.18,
"customerPrice": 3.95
},
{
"minQuantity": 12,
"maxQuantity": 35,
"listPrice": 5.48,
"netPrice": 3.84,
"customerPrice": 3.65
}
],
"decorationPricing": [...]
}
```
---
### Get Product Metafields
Get product identifiers (extra_id, supplier_code, product_id) by extra IDs.
**V2 Endpoint:**
```http
GET /extra/v2/products/metafields
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `extra_id` | string | Comma-separated list of extra IDs |
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products/metafields?extra_id=12345,12346,12347" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products/metafields?extra_id=12345,12346,12347",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 3,
"page": 1,
"pageSize": 20,
"totalPages": 1,
"results": [
{
"extraId": 12345,
"supplierCode": "SanMar",
"productId": "PC61"
},
{
"extraId": 12346,
"supplierCode": "SanMar",
"productId": "PC54"
}
]
}
```
---
### Get Extra ID by Product
Look up the extra ID for a product given supplier code and product ID.
**V2 Endpoint:**
```http
GET /extra/v2/products/get-extra-id/
```
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `supplier_code` | string | Yes | Supplier code |
| `product_id` | string | Yes | Product ID |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products/get-extra-id/?supplier_code=SanMar&product_id=PC61" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products/get-extra-id/?supplier_code=SanMar&product_id=PC61",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"extraId": 12345,
"supplierCode": "SanMar",
"productId": "PC61"
}
```
---
### Get Product by Title
Look up product metafields by supplier code and product title.
**V2 Endpoint:**
```http
GET /extra/v2/products/get-metafields-by-title/
```
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `supplier_code` | string | Yes | Supplier code |
| `title` | string | Yes | Product title to search |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products/get-metafields-by-title/?supplier_code=SanMar&title=Essential%20Tee" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products/get-metafields-by-title/?supplier_code=SanMar&title=Essential%20Tee",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
---
### Get Top-Selling Variants (V1)
Get product variants sorted by sales volume.
**V1 Endpoint:**
```http
GET /extra/v1/products/{product_id}/sorted-parts
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `days` | integer | 30 | Lookback period in days |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v1/products/PC61/sorted-parts?days=90" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v1/products/PC61/sorted-parts?days=90",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 45,
"parts": [
{
"partId": "PC61-BLK-S",
"color": "Black",
"size": "S",
"totalSales": 1250
},
{
"partId": "PC61-BLK-M",
"color": "Black",
"size": "M",
"totalSales": 1180
}
]
}
```
---
### Get Product Classifications (V1)
Get product classifications for various platforms (Shopify, Google, Amazon, Avalara, TaxJar).
**V1 Endpoint:**
```http
GET /extra/v1/products/{extra_id}/classifications
```
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v1/products/12345/classifications" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v1/products/12345/classifications",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"id": 12345,
"name": "Port & Company Essential Tee",
"listPrice": 5.98,
"shopify": {
"productType": "T-Shirts",
"tags": ["apparel", "t-shirt", "cotton"]
},
"google": {
"category": "Apparel & Accessories > Clothing > Shirts & Tops"
},
"amazon": {
"category": "Clothing, Shoes & Jewelry > Men > Clothing > Shirts > T-Shirts"
},
"avalara": {
"taxCode": "PC040100"
},
"taxjar": {
"taxCode": "20010"
}
}
```
---
## Suppliers API
### List Suppliers
Get a paginated list of suppliers with optional filtering.
**V2 Endpoint:**
```http
GET /extra/v2/suppliers
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `shopify_ready` | boolean | Filter suppliers ready for Shopify integration |
| `credentials_available` | boolean | Filter suppliers with available credentials |
| `decorations_available` | boolean | Filter suppliers with decoration data |
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/suppliers?shopify_ready=true&page_size=50" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/suppliers?shopify_ready=true&page_size=50",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 85,
"page": 1,
"pageSize": 50,
"totalPages": 2,
"next": "https://api.psrestful.com/extra/v2/suppliers?shopify_ready=true&page=2&page_size=50",
"previous": null,
"results": [
{
"id": 1,
"code": "SanMar",
"name": "SanMar",
"website": "https://www.sanmar.com",
"shopifyReady": true,
"credentialsAvailable": true,
"decorationsAvailable": true,
"supported": true
}
]
}
```
---
## Brands API
### List Brands
Get brands, optionally filtered by supplier.
**V2 Endpoint:**
```http
GET /extra/v2/brands
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `supplier_id` | integer | Filter by supplier ID |
| `supplier_code` | string | Filter by supplier code |
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/brands?supplier_code=SanMar" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/brands?supplier_code=SanMar",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 45,
"page": 1,
"pageSize": 20,
"totalPages": 3,
"results": [
{
"id": 101,
"name": "Port & Company",
"supplierCode": "SanMar"
},
{
"id": 102,
"name": "Nike",
"supplierCode": "SanMar"
}
]
}
```
---
## Categories API
### List Categories
Get product categories, optionally filtered by supplier.
**V2 Endpoint:**
```http
GET /extra/v2/categories
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `supplier_id` | integer | Filter by supplier ID |
| `supplier_code` | string | Filter by supplier code |
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/categories?supplier_code=SanMar" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/categories?supplier_code=SanMar",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 28,
"page": 1,
"pageSize": 20,
"totalPages": 2,
"results": [
{
"id": 1,
"name": "T-Shirts",
"supplierCode": "SanMar"
},
{
"id": 2,
"name": "Polos",
"supplierCode": "SanMar"
}
]
}
```
---
## Inventory API
### List Inventory
Get cached inventory data for a supplier with efficient filtering.
**V2 Endpoint:**
```http
GET /extra/v2/inventory/{supplier_code}
```
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `supplier_code` | string | Supplier code (e.g., SanMar) |
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
| `product_id` | string | Filter by product ID |
| `part_id` | string | Filter by part ID |
| `last_modified__since` | string | Filter by modification time (e.g., `1h`, `30m`, `2d`, `7d`) |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/inventory/SanMar?product_id=PC61&page_size=100" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/inventory/SanMar?product_id=PC61&page_size=100",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 245,
"page": 1,
"pageSize": 100,
"totalPages": 3,
"results": [
{
"productId": "PC61",
"partId": "PC61-BLK-S",
"quantityAvailable": 15420,
"warehouseId": "WH001",
"warehouseName": "Dallas, TX",
"lastModified": "2025-01-16T10:30:00Z"
}
]
}
```
**Time Filter Formats:**
| Format | Example | Description |
|--------|---------|-------------|
| Minutes | `30m` | Last 30 minutes |
| Hours | `1h`, `24h` | Last N hours |
| Days | `2d`, `7d` | Last N days |
---
## Decorations API
### Get Supplier Decorations
Get available decoration methods and pricing for a supplier.
**V2 Endpoint:**
```http
GET /extra/v2/suppliers/{supplier_code}/decorations
```
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `supplier_code` | string | Supplier code |
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `currency` | string | USD | Currency: `USD` or `CAD` |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/suppliers/SanMar/decorations?currency=USD" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/suppliers/SanMar/decorations?currency=USD",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 12,
"results": [
{
"id": 1,
"name": "Screen Print",
"description": "Traditional screen printing",
"priceBreaks": [
{
"minQuantity": 1,
"maxQuantity": 11,
"price": 3.50,
"setupFee": 25.00
},
{
"minQuantity": 12,
"maxQuantity": 47,
"price": 2.75,
"setupFee": 25.00
}
],
"locations": ["Front", "Back", "Left Sleeve", "Right Sleeve"]
},
{
"id": 2,
"name": "Embroidery",
"description": "Machine embroidery",
"pricePerStitch": 0.0015,
"minimumCharge": 5.00,
"locations": ["Left Chest", "Right Chest", "Cap Front"]
}
]
}
```
---
## Part ID Mappings API
### Get Part ID Mappings
Get cross-reference mappings between part IDs used in different PromoStandards services (Media, Inventory, PPC).
**V2 Endpoint:**
```http
GET /extra/v2/part-id-mappings
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `supplier` | integer | Filter by supplier ID |
| `supplier_code` | string | Filter by supplier code |
| `product_id` | string | Filter by product ID |
| `page` | integer | Page number |
| `page_size` | integer | Items per page |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/part-id-mappings?supplier_code=SanMar&product_id=PC61" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/part-id-mappings?supplier_code=SanMar&product_id=PC61",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"count": 45,
"page": 1,
"pageSize": 20,
"totalPages": 3,
"results": [
{
"productId": "PC61",
"medId": "PC61-BLK-S",
"invId": "PC61BLKS",
"ppcId": "PC61-BLK-S"
},
{
"productId": "PC61",
"medId": "PC61-BLK-M",
"invId": "PC61BLKM",
"ppcId": "PC61-BLK-M"
}
]
}
```
**Use Case:** Different PromoStandards services sometimes use different part ID formats. This endpoint helps you map between them when correlating data across services.
---
## Web Scraper API (V1)
### Scrape Product Data
Scrape additional product data from supplier websites.
**V1 Endpoint:**
```http
GET /extra/v1/scrape/{supplier_code}/
```
**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `supplier_code` | string | Supplier code |
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | Product URL to scrape (must be valid HTTP URL) |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/extra/v1/scrape/SanMar/?url=https://www.sanmar.com/p/PC61" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v1/scrape/SanMar/?url=https://www.sanmar.com/p/PC61",
{
headers: { "X-API-Key": "your-api-key" },
}
);
const data = await response.json();
```
**Response:** `200 OK`
```json
{
"pmsMatch": true,
"height": 2.5,
"width": 12.0,
"casePack": 72,
"caseWeight": 18.5,
"masterCarton": {
"quantity": 144,
"price": 5.25,
"weight": 38.0
},
"openingDiameter": null
}
```
**Note:** This endpoint is useful for obtaining packaging and dimension data not available through standard PromoStandards services.
---
## Error Handling
### Error Response Format
```json
{
"detail": "Error message describing what went wrong"
}
```
### HTTP Status Codes
| Code | Meaning | Common Causes |
|------|---------|---------------|
| `200` | OK | Request succeeded |
| `400` | Bad Request | Invalid parameters |
| `401` | Unauthorized | Missing or invalid authentication |
| `403` | Forbidden | Insufficient permissions |
| `404` | Not Found | Resource not found |
| `429` | Too Many Requests | Rate limit exceeded |
---
## Best Practices
### 1. Use V2 Endpoints
V2 endpoints provide consistent pagination, camelCase responses, and additional features. Use V1 only for features not yet available in V2.
### 2. Paginate Large Requests
Always use pagination for list endpoints. Start with reasonable page sizes (20-50) and implement proper pagination handling.
### 3. Use Filters Efficiently
Filter data at the API level rather than fetching everything and filtering client-side. This reduces bandwidth and improves performance.
### 4. Cache Responses Appropriately
- **Products/Suppliers/Brands/Categories:** Cache for hours or days (changes infrequently)
- **Inventory:** Cache briefly or use `last_modified__since` filter
- **Pricing:** Cache with appropriate TTL based on your business needs
### 5. Use Expand Parameter Wisely
Only request expanded data when needed. Fetching `ppcs` and `inventory` adds processing time.
---
## Related Documentation
- [Credentials API](/credentials-api) - Configure supplier credentials for customer-specific pricing
- [Sub-Accounts API](/sub-accounts-api) - Manage multi-tenant access
- [Authentication](/authentication) - API authentication methods
- [Rate Limits](/rate-limits) - API usage limits
---
## Support
For additional help:
- **Email:** devs@psrestful.com
---
**Last Updated:** January 2025
**API Version:** v1, v2
---
## PSMEDx API
Source: https://docs.psrestful.com/psmedx-api
- **Product Decoration** - Apply artwork to blank product images
- **Color Analysis** - Extract dominant colors and find Pantone matches
- **Image Classification** - Classify images as blank or decorated
- **Vectorization** - Convert raster images to SVG vectors
- **Bounding Box Detection** - Find decoration areas on products
---
## Authentication
All PSMEDx endpoints require authentication.
```http
X-API-Key: your-api-key-here
```
Or:
```http
Authorization: Bearer your-token-here
```
---
## Base URL
```
https://api.psrestful.com/psmedx/v1
```
---
## Image Processing APIs
### Remove Background
Remove the background from a product image, returning a transparent PNG.
**Endpoint:**
```http
GET /psmedx/v1/remove-background/
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `image_url` | string | Required | URL of the image to process |
| `quality` | integer | 85 | Compression quality (1-100) |
| `max_width` | integer | 2000 | Maximum output width (100-5000) |
| `max_height` | integer | 2000 | Maximum output height (100-5000) |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/remove-background/?image_url=https://example.com/product.jpg&quality=90" \
-H "X-API-Key: your-api-key" \
--output product-nobg.png
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/remove-background/",
params={
"image_url": "https://example.com/product.jpg",
"quality": 90
},
headers={"X-API-Key": "your-api-key"}
)
with open("product-nobg.png", "wb") as f:
f.write(response.content)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/remove-background/?" +
new URLSearchParams({
image_url: "https://example.com/product.jpg",
quality: "90"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const blob = await response.blob();
// In Node.js:
// const fs = require("fs");
// fs.writeFileSync("product-nobg.png", Buffer.from(await response.arrayBuffer()));
```
**Response:** `image/png` - Binary PNG image with transparent background
**Use Cases:**
- Prepare product images for e-commerce
- Create consistent product catalogs
- Prepare images for decoration previews
---
### Decorate Product
Apply artwork to a blank product image using bounding box positioning.
**Endpoint:**
```http
GET /psmedx/v1/decorate/
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `blank_url` | string | Required | URL of the blank product image |
| `artwork_url` | string | Required | URL of the artwork/logo to apply |
| `bounding_box_json` | string | Required | JSON string with bounding box coordinates |
| `placement` | string | AUTO | Placement: `AUTO`, `HORIZONTAL`, `VERTICAL` |
| `emboss` | boolean | false | Apply emboss effect to artwork |
| `quality` | integer | 85 | Compression quality (1-100) |
| `max_width` | integer | 2000 | Maximum output width (100-5000) |
| `max_height` | integer | 2000 | Maximum output height (100-5000) |
**Bounding Box JSON Format:**
```json
{
"left": 150,
"upper": 100,
"right": 350,
"lower": 250
}
```
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/decorate/?blank_url=https://example.com/tshirt-blank.jpg&artwork_url=https://example.com/logo.png&bounding_box_json=%7B%22left%22:150,%22upper%22:100,%22right%22:350,%22lower%22:250%7D" \
-H "X-API-Key: your-api-key" \
--output decorated-product.png
```
```python copy
import json
import requests
bounding_box = {"left": 150, "upper": 100, "right": 350, "lower": 250}
response = requests.get(
"https://api.psrestful.com/psmedx/v1/decorate/",
params={
"blank_url": "https://example.com/tshirt-blank.jpg",
"artwork_url": "https://example.com/logo.png",
"bounding_box_json": json.dumps(bounding_box)
},
headers={"X-API-Key": "your-api-key"}
)
with open("decorated-product.png", "wb") as f:
f.write(response.content)
```
```javascript copy
const boundingBox = { left: 150, upper: 100, right: 350, lower: 250 };
const params = new URLSearchParams({
blank_url: "https://example.com/tshirt-blank.jpg",
artwork_url: "https://example.com/logo.png",
bounding_box_json: JSON.stringify(boundingBox)
});
const response = await fetch(
`https://api.psrestful.com/psmedx/v1/decorate/?${params}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const blob = await response.blob();
// In Node.js:
// const fs = require("fs");
// fs.writeFileSync("decorated-product.png", Buffer.from(await response.arrayBuffer()));
```
**Response:** `image/png` - Decorated product image
**Use Cases:**
- Generate product mockups with customer logos
- Create decoration previews for e-commerce
- Build virtual sample generators
---
### Vectorize Image
Convert a raster image to SVG vector format.
**Endpoint:**
```http
GET /psmedx/v1/vectorize/
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `image_url` | string | Required | URL of the raster image |
| `colormode` | string | color | `color` or `binary` |
| `hierarchical` | string | stacked | `stacked` or `cutout` |
| `mode` | string | spline | `spline`, `polygon`, or `none` |
| `filter_speckle` | integer | 4 | Speckle filter threshold (min: 0) |
| `color_precision` | integer | 6 | Color precision level (min: 1) |
| `layer_difference` | integer | 16 | Layer difference threshold (min: 1) |
| `corner_threshold` | integer | 60 | Corner detection threshold (min: 0) |
| `length_threshold` | float | 4.0 | Path length threshold (3.5-10.0) |
| `max_iterations` | integer | 10 | Maximum iterations (min: 1) |
| `splice_threshold` | integer | 45 | Splice threshold (min: 0) |
| `path_precision` | integer | 3 | Path precision (min: 1) |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/vectorize/?image_url=https://example.com/logo.png&colormode=color" \
-H "X-API-Key: your-api-key" \
--output logo.svg
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/vectorize/",
params={
"image_url": "https://example.com/logo.png",
"colormode": "color"
},
headers={"X-API-Key": "your-api-key"}
)
with open("logo.svg", "wb") as f:
f.write(response.content)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/vectorize/?" +
new URLSearchParams({
image_url: "https://example.com/logo.png",
colormode: "color"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const blob = await response.blob();
// In Node.js:
// const fs = require("fs");
// fs.writeFileSync("logo.svg", Buffer.from(await response.arrayBuffer()));
```
**Response:** `image/svg+xml` - SVG vector graphic
**Use Cases:**
- Convert logos to vector format for scaling
- Prepare artwork for screen printing
- Create embroidery-ready files
---
### Get Image Info
Retrieve detailed information about an image including dimensions and DPI.
**Endpoint:**
```http
GET /psmedx/v1/image-info/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `image_url` | string | Required - URL of the image |
| `original_width_in` | float | Optional - Original width in inches |
| `original_height_in` | float | Optional - Original height in inches |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/image-info/?image_url=https://example.com/artwork.png" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/image-info/",
params={
"image_url": "https://example.com/artwork.png"
},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/image-info/?" +
new URLSearchParams({
image_url: "https://example.com/artwork.png"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"fileName": "artwork.png",
"fileSize": 245000,
"contentType": "image/png",
"width": 1200,
"height": 800,
"dpi": 300,
"isHighRes": true
}
```
---
## Color Analysis APIs
### Get Top Colors
Extract the dominant colors from an image.
**Endpoint:**
```http
GET /psmedx/v1/image/colors/
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `image_url` | string | Required | URL of the image to analyze |
| `top_n_colors` | integer | 5 | Number of colors to return |
| `remove_bg` | boolean | false | Remove background before analysis |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/image/colors/?image_url=https://example.com/logo.png&top_n_colors=5&remove_bg=true" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/image/colors/",
params={
"image_url": "https://example.com/logo.png",
"top_n_colors": 5,
"remove_bg": True
},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/image/colors/?" +
new URLSearchParams({
image_url: "https://example.com/logo.png",
top_n_colors: "5",
remove_bg: "true"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"topNColors": 5,
"colors": [
{
"rgb": [0, 82, 147],
"hex": "#005293",
"countPercentage": 45.2,
"pantone": {
"pantone": "PMS 301 C",
"name": "Dark Blue",
"hex": "#00538B",
"rgb": [0, 83, 139]
}
},
{
"rgb": [255, 255, 255],
"hex": "#FFFFFF",
"countPercentage": 32.1,
"pantone": {
"pantone": "PMS White",
"name": "White",
"hex": "#FFFFFF",
"rgb": [255, 255, 255]
}
}
]
}
```
**Use Cases:**
- Match product colors to artwork
- Suggest complementary products
- Validate brand color compliance
---
### Nearest Pantone
Find the nearest Pantone color match for hex colors.
**Endpoint:**
```http
GET /psmedx/v1/nearest-pantone/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `hex_color` | string[] | Required - List of hex color codes (e.g., `#005293`) |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/nearest-pantone/?hex_color=%23005293&hex_color=%23FF5733" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/nearest-pantone/",
params={
"hex_color": ["#005293", "#FF5733"]
},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const params = new URLSearchParams();
params.append("hex_color", "#005293");
params.append("hex_color", "#FF5733");
const response = await fetch(
`https://api.psrestful.com/psmedx/v1/nearest-pantone/?${params}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
[
{
"input": "#005293",
"pantone": "PMS 301 C",
"name": "Dark Blue",
"hex": "#00538B",
"rgb": [0, 83, 139],
"distance": 8.5
},
{
"input": "#FF5733",
"pantone": "PMS 172 C",
"name": "Orange",
"hex": "#FF5A00",
"rgb": [255, 90, 0],
"distance": 12.3
}
]
```
**Use Cases:**
- Convert digital colors to print-ready Pantone
- Ensure brand color consistency
- Generate color specifications for suppliers
---
### Pantone to Hex
Convert Pantone color codes to hexadecimal values.
**Endpoint:**
```http
GET /psmedx/v1/pantone-to-hex/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `pantone` | string[] | Required - List of Pantone codes |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/pantone-to-hex/?pantone=PMS%20301%20C&pantone=PMS%20172%20C" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/pantone-to-hex/",
params={
"pantone": ["PMS 301 C", "PMS 172 C"]
},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const params = new URLSearchParams();
params.append("pantone", "PMS 301 C");
params.append("pantone", "PMS 172 C");
const response = await fetch(
`https://api.psrestful.com/psmedx/v1/pantone-to-hex/?${params}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
[
{
"pantone": "PMS 301 C",
"name": "Dark Blue",
"hex": "#00538B",
"rgb": [0, 83, 139]
},
{
"pantone": "PMS 172 C",
"name": "Orange",
"hex": "#FF5A00",
"rgb": [255, 90, 0]
}
]
```
---
## Image Classification APIs
### Classify Image
Analyze an image to determine if it's blank or decorated, and extract metadata.
**Endpoint:**
```http
GET /psmedx/v1/classify/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `image_url` | string | Required - URL of the image to classify |
| `part_colors_json` | string | Optional - JSON with part-specific colors |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/classify/?image_url=https://example.com/product.jpg" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/classify/",
params={
"image_url": "https://example.com/product.jpg"
},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/classify/?" +
new URLSearchParams({
image_url: "https://example.com/product.jpg"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"fileName": "product.jpg",
"fileSize": 125000,
"contentType": "image/jpeg",
"height": 800,
"width": 600,
"dpi": 150,
"isHighRes": false,
"blankOrDecorated": {
"blank": 0.15,
"decorated": 0.85
},
"singlePartOrGroup": {
"group": 0.05,
"singlePart": 0.95
},
"boundingBoxes": [
{
"left": 150,
"upper": 100,
"right": 350,
"lower": 250
}
],
"colors": [
{
"rgb": [0, 0, 0],
"hex": "#000000",
"count": 45000,
"name": "Black",
"countPercentage": 35.5
}
],
"colorName": "Black"
}
```
**Response Fields:**
| Field | Description |
|-------|-------------|
| `blankOrDecorated` | Confidence scores (0-1) for blank vs decorated classification |
| `singlePartOrGroup` | Confidence scores for single part vs group shot |
| `boundingBoxes` | Detected decoration/logo areas |
| `colors` | Dominant colors in the image |
| `isHighRes` | Whether image meets high-resolution threshold |
---
## Bounding Box APIs
### Find Bounding Box
Compare blank and decorated images to detect the decoration area.
**Endpoint:**
```http
GET /psmedx/v1/find-bounding-box/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `blank_url` | string | Required - URL of the blank product image |
| `decorated_url` | string | Required - URL of the decorated product image |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/find-bounding-box/?blank_url=https://example.com/blank.jpg&decorated_url=https://example.com/decorated.jpg" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/find-bounding-box/",
params={
"blank_url": "https://example.com/blank.jpg",
"decorated_url": "https://example.com/decorated.jpg"
},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/find-bounding-box/?" +
new URLSearchParams({
blank_url: "https://example.com/blank.jpg",
decorated_url: "https://example.com/decorated.jpg"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
**Response:** `200 OK`
```json
{
"boundingBox": {
"left": 150,
"upper": 100,
"right": 350,
"lower": 250
},
"confidence": 0.95
}
```
**Use Cases:**
- Automatically detect decoration areas
- Create bounding box templates for new products
- Validate decoration placement
---
### Show Bounding Boxes
Draw bounding boxes on an image for visualization.
**Endpoint:**
```http
GET /psmedx/v1/show-bounding-boxes/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `image_url` | string | Required - URL of the image |
| `bounding_boxes_json` | string | Required - JSON array of bounding boxes |
**Bounding Boxes JSON Format:**
```json
[
{"left": 150, "upper": 100, "right": 350, "lower": 250},
{"left": 400, "upper": 300, "right": 550, "lower": 400}
]
```
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/show-bounding-boxes/?image_url=https://example.com/product.jpg&bounding_boxes_json=%5B%7B%22left%22:150,%22upper%22:100,%22right%22:350,%22lower%22:250%7D%5D" \
-H "X-API-Key": "your-api-key" \
--output product-with-boxes.png
```
```python copy
import json
import requests
bounding_boxes = [
{"left": 150, "upper": 100, "right": 350, "lower": 250}
]
response = requests.get(
"https://api.psrestful.com/psmedx/v1/show-bounding-boxes/",
params={
"image_url": "https://example.com/product.jpg",
"bounding_boxes_json": json.dumps(bounding_boxes)
},
headers={"X-API-Key": "your-api-key"}
)
with open("product-with-boxes.png", "wb") as f:
f.write(response.content)
```
```javascript copy
const boundingBoxes = [
{ left: 150, upper: 100, right: 350, lower: 250 }
];
const params = new URLSearchParams({
image_url: "https://example.com/product.jpg",
bounding_boxes_json: JSON.stringify(boundingBoxes)
});
const response = await fetch(
`https://api.psrestful.com/psmedx/v1/show-bounding-boxes/?${params}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const blob = await response.blob();
// In Node.js:
// const fs = require("fs");
// fs.writeFileSync("product-with-boxes.png", Buffer.from(await response.arrayBuffer()));
```
**Response:** `image/png` - Image with bounding boxes drawn
---
### Find and Show Bounding Box
Combined operation: detect bounding box and return the image with it drawn.
**Endpoint:**
```http
GET /psmedx/v1/find-and-show-bounding-box/
```
**Query Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `blank_url` | string | Required - URL of the blank product image |
| `decorated_url` | string | Required - URL of the decorated product image |
**Example Request:**
```bash copy
curl -X GET "https://api.psrestful.com/psmedx/v1/find-and-show-bounding-box/?blank_url=https://example.com/blank.jpg&decorated_url=https://example.com/decorated.jpg" \
-H "X-API-Key: your-api-key" \
--output product-with-detected-box.png
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/psmedx/v1/find-and-show-bounding-box/",
params={
"blank_url": "https://example.com/blank.jpg",
"decorated_url": "https://example.com/decorated.jpg"
},
headers={"X-API-Key": "your-api-key"}
)
with open("product-with-detected-box.png", "wb") as f:
f.write(response.content)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/psmedx/v1/find-and-show-bounding-box/?" +
new URLSearchParams({
blank_url: "https://example.com/blank.jpg",
decorated_url: "https://example.com/decorated.jpg"
}),
{
headers: { "X-API-Key": "your-api-key" }
}
);
const blob = await response.blob();
// In Node.js:
// const fs = require("fs");
// fs.writeFileSync("product-with-detected-box.png", Buffer.from(await response.arrayBuffer()));
```
**Response:** `image/png` - Blank image with detected bounding box drawn
---
## Image Compression Options
Most image-returning endpoints support compression parameters:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `quality` | integer | 85 | JPEG/WebP quality (1-100). Higher = better quality, larger file |
| `max_width` | integer | 2000 | Maximum width in pixels (100-5000) |
| `max_height` | integer | 2000 | Maximum height in pixels (100-5000) |
**Notes:**
- PNG output maintains transparency
- Images are proportionally scaled to fit within max dimensions
- Quality setting primarily affects JPEG/WebP output
---
## Error Handling
### Error Response Format
```json
{
"detail": "Error message describing what went wrong"
}
```
### HTTP Status Codes
| Code | Meaning | Common Causes |
|------|---------|---------------|
| `200` | OK | Request succeeded |
| `400` | Bad Request | Invalid parameters, malformed URL |
| `401` | Unauthorized | Missing authentication |
| `403` | Forbidden | Invalid API key |
| `404` | Not Found | Image URL not accessible |
| `422` | Unprocessable Entity | Invalid image format or corrupted file |
| `429` | Too Many Requests | Rate limit exceeded |
| `500` | Server Error | Processing error |
### Common Errors
**Invalid Image URL:**
```json
{
"detail": "Unable to fetch image from provided URL"
}
```
**Unsupported Format:**
```json
{
"detail": "Unsupported image format. Supported: PNG, JPEG, WebP, GIF"
}
```
**Invalid Bounding Box:**
```json
{
"detail": "Invalid bounding box: coordinates must be positive integers"
}
```
---
## Best Practices
### 1. Use Appropriate Quality Settings
- **Web display:** quality=75-85, max dimensions 1200-1500px
- **Print/high-res:** quality=90-100, max dimensions 2000-5000px
- **Thumbnails:** quality=70, max dimensions 300-500px
### 2. Cache Results
PSMEDx operations are computationally intensive. Cache processed images when possible to improve performance and reduce API calls.
### 3. Validate Image URLs
Ensure image URLs are publicly accessible and return valid image content before calling PSMEDx APIs.
### 4. Use Bounding Boxes for Consistency
Store bounding box coordinates for product templates to ensure consistent decoration placement across orders.
### 5. Handle Large Images
For very large images, use the `max_width` and `max_height` parameters to reduce processing time and response size.
---
## Use Case Examples
### E-commerce Product Mockup Generator
```python copy
import json
import requests
API_KEY = "your-api-key"
BASE_URL = "https://api.psrestful.com/psmedx/v1"
def generate_mockup(blank_url, logo_url, bounding_box):
"""Generate a decorated product mockup."""
response = requests.get(
f"{BASE_URL}/decorate/",
params={
"blank_url": blank_url,
"artwork_url": logo_url,
"bounding_box_json": json.dumps(bounding_box),
"quality": 90
},
headers={"X-API-Key": API_KEY}
)
if response.status_code == 200:
return response.content # PNG image bytes
else:
raise Exception(f"Error: {response.json()}")
# Usage
bounding_box = {"left": 150, "upper": 100, "right": 350, "lower": 250}
mockup = generate_mockup(
"https://example.com/tshirt-blank.jpg",
"https://example.com/customer-logo.png",
bounding_box
)
```
```javascript copy
const API_KEY = "your-api-key";
const BASE_URL = "https://api.psrestful.com/psmedx/v1";
async function generateMockup(blankUrl, logoUrl, boundingBox) {
const params = new URLSearchParams({
blank_url: blankUrl,
artwork_url: logoUrl,
bounding_box_json: JSON.stringify(boundingBox),
quality: "90"
});
const response = await fetch(
`${BASE_URL}/decorate/?${params}`,
{
headers: { "X-API-Key": API_KEY }
}
);
if (response.ok) {
return await response.blob(); // PNG image
} else {
throw new Error(`Error: ${await response.text()}`);
}
}
// Usage
const boundingBox = { left: 150, upper: 100, right: 350, lower: 250 };
const mockup = await generateMockup(
"https://example.com/tshirt-blank.jpg",
"https://example.com/customer-logo.png",
boundingBox
);
```
### Brand Color Compliance Checker
```python copy
def check_brand_colors(logo_url, brand_pantones):
"""Check if logo colors match brand guidelines."""
# Get top colors from logo
response = requests.get(
f"{BASE_URL}/image/colors/",
params={
"image_url": logo_url,
"top_n_colors": 5,
"remove_bg": True
},
headers={"X-API-Key": API_KEY}
)
colors = response.json()["colors"]
# Check each color against brand Pantones
for color in colors:
if color["pantone"] and color["pantone"]["pantone"] in brand_pantones:
print(f"Match: {color['pantone']['pantone']} matches brand guidelines")
else:
print(f"Warning: {color['hex']} may not match brand guidelines")
```
```javascript copy
async function checkBrandColors(logoUrl, brandPantones) {
const params = new URLSearchParams({
image_url: logoUrl,
top_n_colors: "5",
remove_bg: "true"
});
const response = await fetch(
`${BASE_URL}/image/colors/?${params}`,
{
headers: { "X-API-Key": API_KEY }
}
);
const { colors } = await response.json();
for (const color of colors) {
if (color.pantone && brandPantones.includes(color.pantone.pantone)) {
console.log(`Match: ${color.pantone.pantone} matches brand guidelines`);
} else {
console.log(`Warning: ${color.hex} may not match brand guidelines`);
}
}
}
```
---
## Related Documentation
- [Extra APIs](/extra-apis) - Additional product and supplier APIs
- [Credentials API](/credentials-api) - Configure supplier credentials
- [Authentication](/authentication) - API authentication methods
---
## Support
For additional help:
- **Email:** devs@psrestful.com
---
**Last Updated:** January 2025
**API Version:** v1
---
## Design Decisions
Source: https://docs.psrestful.com/design-decisions
to the API when we have the suppliers credentials and the Service/Method to be called is not sensitive.
## HTTP Verbs
We only use `GET` and `POST`. We use `GET` for all queries:
- Product Data Service
- Inventory Service
- PPC Service
- Media Content Service
- Order Status Service
- Invoice Service
We use `POST` only for creating Purchase Orders:
- Purchase Order Service(sendPO Method)
## About REST
In order to be more restful, we should have use the following conventions in the product data:
we should have create an endpoint called `/products` and be able to use the query parameters to filter the results like this:
```url
/products?sellable=true
```
or
```url
/products?closeout=true
```
for media content we should have used the following endpoint:
```url
/products//medias?modifed_since=2021-09-14T00:00:00
```
and we will have both methods in only one comprehensive endpoint.
But in this case `getMediaContent` allows to filter by part id, by media type and class type while `GetMediaDateModifiedRequest` doesn't
allow to filter by part id, media type and class type.
Because in order to be really `REST` and fast we should have the data cached and this service is a basic proxy we decided to keep more close to the
Standard.
We did some minor modification in the names like:
- `/medias` instead of `getMediaContent`
- `/sellable-products` instead of `getProductSellable`
etc.
The [Extra APIs](/extra-apis) are gradually implementing a more RESTful approach with caching:
- **Products:** `/extra/v2/products` consolidates product data with filtering and the `expand` parameter for PPCs, inventory, and media in a single request
- **Inventory:** `/extra/v2/inventory/{supplier_code}` provides cached inventory with efficient querying and time-based filters
- **Pricing:** `/extra/v2/products/{product_id}/ppcs` offers consolidated pricing data
These APIs complement the proxy layer by providing cached, RESTful access to data that has been aggregated and normalized from multiple PromoStandards services.
---
## Guides
Source: https://docs.psrestful.com/guides
# Guides to help you get started with our API
## Product Data
- [How to get all sellable products](guides/product-data/how-to-get-all-sellable-products)
- [How to get product details](guides/product-data/how-to-get-product)
- [How to get products modified since a date](guides/product-data/how-to-get-products-modified-since)
- [How to get closeout products](guides/product-data/how-to-get-closeout-products)
## Media Content
- [How to get product media](guides/media-content/how-to-get-product-media-content)
- [How to get media modified since a date](guides/media-content/how-to-get-media-modified-since)
## PPC
- [How to get all available charges](guides/ppc/how-to-get-all-available-charges)
- [How to get all available locations](guides/ppc/how-to-get-all-available-locations)
- [How to get decoration colors](guides/ppc/how-to-get-decoration-colors)
- [How to get FOB points](guides/ppc/how-to-get-fob-points)
- [How to get configuration and pricing](guides/ppc/how-to-get-pricing-and-configuration)
## Inventory
- [How to get inventory levels](guides/inventory/how-to-get-inventory-levels)
- [How to get filter values](guides/inventory/how-to-filter-values)
## Purchase Order
- [How to send a blank PO](guides/purchase-order/send-po/place-blank-order)
- [How to send a configured PO](guides/purchase-order/send-po/place-configured-order)
- [How to send a sample PO](guides/purchase-order/send-po/place-sample-order)
## Order Status
- [How to get order status details](guides/order-status/how-to-get-order-status-details)
- [How to get order status types](guides/order-status/how-to-get-order-status-types)
## Order Shipment Notification
- [How to get order shipment notifications](guides/order-shipment-notification/how-to-get-order-shipment-notifications)
## Invoice
- [How to get invoices](guides/invoice/how-to-get-invoices)
---
## PromoStandards Documentation
Source: https://docs.psrestful.com/standards
# Documentation from PromoStandards.org website
This documentation is just a friendly version of the documentation found on the [PromoStandards.org](http://www.promostandards.org) website.
The direct links to the documentation are:
[PromoStandards Tools](http://tools.promostandards.org/)
## Documentation
- [Product Data 1.0.0](standards/product-data-1.0.0)
- [Product Data 2.0.0](standards/product-data-2.0.0)
- [Media Content 1.1.0](standards/media-content-1.1.0)
- [PPC 1.0.0](standards/ppc-1.0.0)
- [Inventory 1.2.1](standards/inventory-1.2.1)
- [Inventory 2.0.0](standards/inventory-2.0.0)
- [Product Compliance 1.0.0](standards/product-compliance-1.0.0),
- [Purchase Order 1.0.0](standards/purchase-order-1.0.0)
- [Order Status 1.0.0](standards/order-status-1.0.0)
- [Order Status 2.0.0](standards/order-status-2.0.0)
- [Order Shipment Notification 1.0.0](standards/order-shipment-notification-1.0.0)
- [Order Shipment Notification 2.0.0](standards/order-shipment-notification-2.0.0)
- [Invoice 1.0.0](standards/invoice-1.0.0)
---
## How to get filter values for a product
Source: https://docs.psrestful.com/guides/inventory/how-to-filter-values
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/inventory/filter-values/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `product_id_type` | string | No | Type of product ID being used |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/filter-values/55414/" \
-H "X-API-Key: your-api-key"
```
```python copy
)
print(response.json())
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/filter-values/55414/",
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `55414`
```json copy
{
"FilterValues": {
"productId": "55414",
"Filter": [
{
"filterType": "COLOR",
"filterValue": [
"WHITE",
"BLACK",
"RED",
"NAVY BLUE",
"SILVER",
"TEAL"
]
},
{
"filterType": "SIZE",
"filterValue": [
"14OZ"
]
}
]
},
"ServiceMessageArray": null
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `productId` | Product identifier |
| `Filter` | Array of filter types and their available values |
| `filterType` | Type of filter (e.g., `COLOR`, `SIZE`) |
| `filterValue` | Array of available values for this filter type |
## Common Filter Types
| Type | Description |
|------|-------------|
| `COLOR` | Available colors for the product |
| `SIZE` | Available sizes (for apparel, drinkware, etc.) |
| `LABEL_SIZE` | Label size designations |
## Typical Workflow
1. Call this endpoint to get available filter values
2. Build a filter UI based on the response
3. Use selected filters when calling [Get Inventory Levels](/guides/inventory/how-to-get-inventory-levels)
```python copy
# Example: Build filters and query inventory
filter_response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/filter-values/55414/",
headers={"X-API-Key": "your-api-key"}
)
filters = filter_response.json()["FilterValues"]["Filter"]
# Step 2: User selects "WHITE" color
selected_color = "WHITE"
# Step 3: Query inventory with filter
inventory_response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/55414/",
params={"color": selected_color},
headers={"X-API-Key": "your-api-key"}
)
```
```javascript copy
// Step 1: Get filter values
const filterResponse = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/filter-values/55414/",
{
headers: { "X-API-Key": "your-api-key" }
}
);
const filters = (await filterResponse.json()).FilterValues.Filter;
// Step 2: User selects "WHITE" color
const selectedColor = "WHITE";
// Step 3: Query inventory with filter
const inventoryResponse = await fetch(
`https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/55414/?color=${selectedColor}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await inventoryResponse.json();
console.log(data);
```
## Use Cases
- **Dynamic filter UIs**: Build dropdown menus based on available options
- **Validation**: Ensure filter values are valid before querying
- **Product displays**: Show available colors/sizes to customers
## Related Guides
- [Get Inventory Levels](/guides/inventory/how-to-get-inventory-levels) - Use filters to query inventory
---
## How to get inventory levels for a product
Source: https://docs.psrestful.com/guides/inventory/how-to-get-inventory-levels
## Function getInventoryLevels
In order to get inventory for a given product we need to use Inventory service (INV). Our API allows easy access by a `GET` request to our `inventory` endpoint.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/inventory/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `part_id` | string[] | No | Filter by specific part ID(s). Can be repeated for multiple parts |
| `color` | string[] | No | Filter by color(s). Can be repeated for multiple colors |
| `size` | string[] | No | Filter by size(s). Can be repeated for multiple sizes |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/55414/" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/55414/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example with Filters
Get inventory for specific colors:
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/55414/?color=WHITE&color=BLACK" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
params = {"color": ["WHITE", "BLACK"]}
response = requests.get(url, headers=headers, params=params)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/55414/?color=WHITE&color=BLACK";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `55414`
The response has been truncated to show 2 parts:
```json copy
{
"Inventory": {
"productId": "55414",
"PartInventoryArray": {
"PartInventory": [
{
"partId": "5414SWHT",
"mainPart": true,
"partColor": "WHITE",
"labelSize": null,
"partDescription": "14OZ ELVATION MUG WHT 77-87047",
"quantityAvailable": {
"Quantity": {
"value": 26,
"uom": "EA"
}
},
"manufacturedItem": false,
"buyToOrder": false,
"replenishmentLeadTime": null,
"lastModified": "2023-07-28T15:29:00+00:00",
"InventoryLocationArray": {
"InventoryLocation": [
{
"inventoryLocationId": "42",
"inventoryLocationName": "WEAR MAGIC",
"inventoryLocationQuantity": {
"Quantity": {
"value": 15,
"uom": "EA"
}
},
"Address": {
"city": "FAIRFIELD",
"country": "US",
"postalCode": "45014"
}
},
{
"inventoryLocationId": "10",
"inventoryLocationName": "BRYAN DAIRY",
"inventoryLocationQuantity": {
"Quantity": {
"value": 11,
"uom": "EA"
}
},
"Address": {
"city": "LARGO",
"country": "US",
"postalCode": "33777"
}
}
]
}
},
{
"partId": "5414SBLK",
"mainPart": true,
"partColor": "BLACK",
"labelSize": null,
"partDescription": "14OZ ELVATION MUG BLK 77-63605",
"quantityAvailable": {
"Quantity": {
"value": 18,
"uom": "EA"
}
},
"manufacturedItem": false,
"buyToOrder": false,
"lastModified": "2023-07-28T15:29:00+00:00",
"InventoryLocationArray": {
"InventoryLocation": [
{
"inventoryLocationId": "42",
"inventoryLocationName": "WEAR MAGIC",
"inventoryLocationQuantity": {
"Quantity": {
"value": 14,
"uom": "EA"
}
},
"Address": {
"city": "FAIRFIELD",
"country": "US",
"postalCode": "45014"
}
}
]
}
}
]
}
},
"ServiceMessageArray": null
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `productId` | Product identifier |
| `PartInventoryArray` | Array of inventory by part/variant |
| `partId` | Part/variant identifier |
| `mainPart` | Whether this is the main part |
| `partColor` | Color of the part |
| `labelSize` | Size label (for apparel) |
| `partDescription` | Description of the part |
| `quantityAvailable` | Total quantity available across all locations |
| `lastModified` | When inventory was last updated |
| `InventoryLocationArray` | Breakdown by warehouse location |
## API Versions
Both v1.2.1 and v2.0.0 are supported:
```url copy
https://api.psrestful.com/v1.2.1/suppliers/{SUPPLIER_CODE}/inventory/{PRODUCT_ID}/
https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/inventory/{PRODUCT_ID}/
```
**Note:** The `part_id` filter is only available in v2.0.0.
## Use Cases
- **Order validation**: Check stock before placing orders
- **Real-time availability**: Display inventory to customers
- **Multi-location shipping**: Find which warehouse has stock
- **Low stock alerts**: Monitor inventory levels
## Related Guides
- [Get Filter Values](/guides/inventory/how-to-filter-values) - Get available filter options for a product
---
## How to get invoices for a given supplier
Source: https://docs.psrestful.com/guides/invoice/how-to-get-invoices
# Function getInvoices
In order to get `all invoices` for a given supplier we need to use Invoice Service(INVC). Our API allows easy access by a `GET` request to our `invoices` endpoint.
## Query Types
| query_type | Short Name | PSRESTful | Description |
|--------------|---------------------------|-----------|-------------------------------------------------------------------------------------------------------------------|
| 1 | PO Number Search | PO_NUMBER | Search for invoices by purchase order number. |
| 2 | Invoice Number Search | INVOICE_NUMBER | Search for invoice by invoice number. |
| 3 | Date Search | DATE | Search for invoices with an invoice date specified by the `requestedDate`. |
| 4 | Available Date Time Search | AVAILABLE_DATE_TIME | Search for invoices that were made available by a date time greater than the value specified by `availableTimeStamp`. |
---
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/invoices/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/PCNA/invoices/?query_type=DATE&requestedDate=2024-01-15" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/invoices/",
params={"query_type": "DATE", "requestedDate": "2024-01-15"},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/invoices/?query_type=DATE&requestedDate=2024-01-15",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response:
This is the response from calling with:
`SUPPLIER_CODE` = `PCNA`
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/PCNA/invoices
```
Example of error message
```json copy
{
"InvoiceArray": null,
"ServiceMessageArray": {
"ServiceMessage": [
{
"code": 999,
"description": "Max 14 days of invoices or invalid date search",
"severity": "Error"
}
]
}
}
```
---
## How to get media modified since a date
Source: https://docs.psrestful.com/guides/media-content/how-to-get-media-modified-since
## Function getMediaDateModified
This endpoint returns a list of product IDs whose media content has been modified since the specified datetime. Use this for incremental media updates.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v1.1.0/suppliers/{SUPPLIER_CODE}/media-modified-since/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `since` | datetime | Yes | ISO 8601 datetime (e.g., `2024-01-15T00:00:00`) |
| `culture_name` | string | No | Language culture name (default: `us_en`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.1.0/suppliers/HIT/media-modified-since/?since=2024-12-01T00:00:00" \
-H "X-API-Key: your-api-key"
```
```python copy
)
print(response.json())
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.1.0/suppliers/HIT/media-modified-since/?since=2024-12-01T00:00:00",
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
### Example Response
```json copy
{
"MediaDateModifiedArray": {
"MediaDateModified": [
{
"productId": "5989"
},
{
"productId": "6234"
},
{
"productId": "7891"
}
]
},
"ServiceMessageArray": null
}
```
## Response Structure
The response contains a `MediaDateModifiedArray` with `MediaDateModified` items:
| Field | Description |
|-------|-------------|
| `productId` | The product identifier whose media has been modified since the specified date |
## Typical Workflow
1. **Initial sync**: Fetch media for all products using [getMediaContent](/guides/media-content/how-to-get-product-media-content)
2. **Store last sync time**: Record when you last synchronized media
3. **Incremental sync**: Use `media-modified-since` with your last sync time
4. **Update changed media**: Fetch media only for products returned by this endpoint
```python copy
# Example incremental media sync workflow
from datetime import datetime
# Your last sync timestamp
last_sync = datetime(2024, 12, 1, 0, 0, 0)
# Get products with modified media
response = requests.get(
f"https://api.psrestful.com/v1.1.0/suppliers/HIT/media-modified-since/",
params={"since": last_sync.isoformat()},
headers={"X-API-Key": "your-api-key"}
)
modified_products = response.json()["MediaDateModifiedArray"]["MediaDateModified"]
# Update media only for changed products
for product in modified_products:
product_id = product["productId"]
# Fetch updated media for this product
media_response = requests.get(
f"https://api.psrestful.com/v1.1.0/suppliers/HIT/medias/{product_id}/",
headers={"X-API-Key": "your-api-key"}
)
update_media_in_catalog(product_id, media_response.json())
# Update your last sync time
last_sync = datetime.now()
```
```javascript copy
// Example incremental media sync workflow
const lastSync = "2024-12-01T00:00:00";
// Get products with modified media
const response = await fetch(
`https://api.psrestful.com/v1.1.0/suppliers/HIT/media-modified-since/?since=${lastSync}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const result = await response.json();
const modifiedProducts = result.MediaDateModifiedArray.MediaDateModified;
// Update media only for changed products
for (const product of modifiedProducts) {
const productId = product.productId;
const mediaResponse = await fetch(
`https://api.psrestful.com/v1.1.0/suppliers/HIT/medias/${productId}/`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
await updateMediaInCatalog(productId, await mediaResponse.json());
}
```
## Use Cases
- **Media library synchronization**: Keep your product images in sync without full refreshes
- **CDN cache invalidation**: Identify which product images need cache updates
- **Image processing pipelines**: Trigger re-processing only for updated media
- **Catalog management**: Track when product visuals change
## Best Practices
1. **Store timestamps**: Always store your last successful sync timestamp
2. **Use reasonable intervals**: For large date ranges, consider a full sync instead
3. **Handle empty responses**: If no media has changed, the array will be empty
4. **Combine with getMediaContent**: After getting modified product IDs, fetch their full media using [getMediaContent](/guides/media-content/how-to-get-product-media-content)
## Related Guides
- [How to get product media content](/guides/media-content/how-to-get-product-media-content) - Get media for specific products
---
## How to get product media content
Source: https://docs.psrestful.com/guides/media-content/how-to-get-product-media-content
## Function getMediaContent
In order to get all media content (images, videos, documents) for a given product we need to use Media Content Service (MED). Our API allows easy access by a `GET` request to our `medias` endpoint.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v1.1.0/suppliers/{SUPPLIER_CODE}/medias/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `media_type` | string | No | Filter by media type: `Image`, `Video`, `Audio`, `Document` |
| `part_id` | string | No | Filter media for a specific part/variant |
| `class_type` | integer | No | Filter by class type ID (can be comma-separated for multiple) |
| `culture_name` | string | No | Language culture name (e.g., `en-US`, `fr-FR`). Default: `en-US` |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Common Class Types
| ID | Name | Description |
|----|------|-------------|
| 1001 | Blank | Undecorated product image |
| 1002 | Decorated | Product with decoration/imprint |
| 1006 | Primary | Main product image |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.1.0/suppliers/HIT/medias/5989/" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v1.1.0/suppliers/HIT/medias/5989/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example with Filters
Get only blank images for a specific part:
```bash copy
curl -X GET "https://api.psrestful.com/v1.1.0/suppliers/HIT/medias/5989/?media_type=Image&class_type=1001&part_id=5989BLK" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
params = {"media_type": "Image", "class_type": "1001", "part_id": "5989BLK"}
response = requests.get(url, headers=headers, params=params)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v1.1.0/suppliers/HIT/medias/5989/?media_type=Image&class_type=1001&part_id=5989BLK";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `5989`
The response has been truncated to show 3 representative media items (Primary, Decorated, and Blank class types):
```json copy
{
"MediaContentArray": {
"MediaContent": [
{
"productId": "5989",
"partId": "5989",
"url": "https://www.hitpromo.net/imageManager/show/5989_group.jpg",
"mediaType": "Image",
"fileSize": null,
"width": null,
"height": null,
"dpi": null,
"color": null,
"description": null,
"singlePart": false,
"changeTimeStamp": null,
"ClassTypeArray": {
"ClassType": [
{
"classTypeId": 1006,
"classTypeName": "Primary"
}
]
},
"DecorationArray": null,
"LocationArray": null
},
{
"productId": "5989",
"partId": "5989BLK",
"url": "https://www.hitpromo.net/imageManager/show/5989_BLK_Horizontal_Silkscreen.jpg",
"mediaType": "Image",
"fileSize": null,
"width": null,
"height": null,
"dpi": null,
"color": null,
"description": "Horizontal Silkscreen Imprint",
"singlePart": false,
"changeTimeStamp": null,
"ClassTypeArray": {
"ClassType": [
{
"classTypeId": 1002,
"classTypeName": "Decorated"
}
]
},
"DecorationArray": {
"Decoration": [
{
"decorationId": 54,
"decorationName": "Silk-Screen"
}
]
},
"LocationArray": null
},
{
"productId": "5989",
"partId": "5989BLK",
"url": "https://www.hitpromo.net/imageManager/show/5989_BLK_Blank.jpg",
"mediaType": "Image",
"fileSize": null,
"width": null,
"height": null,
"dpi": null,
"color": null,
"description": null,
"singlePart": false,
"changeTimeStamp": null,
"ClassTypeArray": {
"ClassType": [
{
"classTypeId": 1001,
"classTypeName": "Blank"
}
]
},
"DecorationArray": {
"Decoration": [
{
"decorationId": 51,
"decorationName": "Blank"
}
]
},
"LocationArray": null
}
]
},
"ErrorMessage": null
}
```
## Response Structure
Each `MediaContent` item contains:
| Field | Description |
|-------|-------------|
| `productId` | The product identifier |
| `partId` | The specific part/variant identifier |
| `url` | Direct URL to the media file |
| `mediaType` | Type of media: `Image`, `Video`, `Audio`, `Document` |
| `ClassTypeArray` | Classification of the media (Blank, Decorated, Primary, etc.) |
| `DecorationArray` | Decoration methods shown in the media |
| `description` | Optional description of the media |
## API Versions
Both v1.0.0 and v1.1.0 are supported:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/medias/{PRODUCT_ID}/
https://api.psrestful.com/v1.1.0/suppliers/{SUPPLIER_CODE}/medias/{PRODUCT_ID}/
```
## Related Guides
- [How to get media modified since a date](/guides/media-content/how-to-get-media-modified-since) - Track media changes over time
---
## OAuth2 Integration Guide
Source: https://docs.psrestful.com/guides/oauth2-integration-guide
# OAuth2 Integration Guide
## Overview
PSRESTful accepts OAuth2 **bearer tokens** for authenticated requests. This guide covers the
**client-credentials** grant — the machine-to-machine (M2M) flow you use when *your own
backend* (an ERP, storefront sync job, or middleware) calls the API on behalf of your account.
You create a **Developer App** in the dashboard, receive a `client_id` and `client_secret`,
exchange them for a short-lived access token, and send that token as a `Bearer` header.
> **TL;DR for engineers**
> 1. Dashboard → **Account Settings → Developer Apps → Create App** → copy `client_id` / `client_secret`.
> 2. `POST https://auth.psrestful.com/oauth/token` with `grant_type=client_credentials`, `audience=psrestful`.
> 3. Send `Authorization: Bearer ` to `https://api.psrestful.com/...`.
> 4. **Cache the token** until it nears `expires_in` (24h). Don't mint one per request.
### When to use OAuth2 vs. an API key
| Use case | Works with | Recommended |
|----------|------------|-------------|
| Browser / storefront reads | Public API key (origin-locked) | Public API key — **never** a client secret in a browser |
| Server-side reads (catalog, inventory, pricing, media, status, invoices) | Private API key · OAuth2 | Either |
| Submitting **purchase orders** | Private API key · OAuth2 | **OAuth2** |
| Account / admin endpoints (`/me`, `/credentials/*`, `/subaccounts/*`) | Private API key · OAuth2 | **OAuth2** |
OAuth2 is **recommended, not required**, for purchase orders and account operations — a private API
key works there too. We recommend OAuth2 because tokens are **short-lived, rotatable, and scoped to a
single app**, so you avoid embedding a long-lived static secret in your ERP. The one hard rule: the
**public (browser) API key cannot** submit POs or call account/admin endpoints — use a private key or
OAuth2 for those.
A client secret is a **confidential** credential. Never ship it to a browser or mobile bundle; for
in-browser calls use the public API key flow described in [Authentication](/authentication).
---
## 1. Prerequisites
- A PSRESTful account.
- A user with the **Admin** or **Developer** role on that account (only these roles can manage
Developer Apps). See [Roles & Permissions](/roles).
---
## 2. Create a Developer App
1. Open the dashboard and go to **Account Settings → Developer Apps**.
2. Click **Create App**.
3. Give it a **Name** (e.g. `erp-sync`) and an optional description, then **Create**.
4. A **Client Secret** is shown **once** — copy it now and store it somewhere safe (a secrets
manager, not source control).

Your app now appears in the list with its **Client ID**. Open the app's **Settings** tab (the gear
icon) at any time to copy the Client ID, reveal the secret again (admins only), or **rotate** it.

| Field | Where it's used |
|-------|-----------------|
| **Client ID** | Public identifier of the app. Safe to log. |
| **Client Secret** | Confidential. Used only server-side to obtain tokens. Rotatable; revealable by admins. |
| **Token URL** | `https://auth.psrestful.com/oauth/token` |
| **Audience** | `psrestful` |
> Each Developer App is bound to **one account**. A token minted from the app authorizes as that
> account — the same account whose supplier credentials and plan limits apply.
---
## 3. Request an access token
Exchange your credentials for a token using the `client_credentials` grant. The app's **Endpoints**
tab shows your exact token URL, audience, and a copy-paste example:

```bash
curl -X POST "https://auth.psrestful.com/oauth/token" \
-H "content-type: application/json" \
-d '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "psrestful",
"grant_type": "client_credentials"
}'
```
Response:
```json
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVC...",
"expires_in": 86400,
"token_type": "Bearer"
}
```
- `expires_in` is in **seconds** (86400 = 24 hours).
- The token is a signed JWT. You don't need to parse it — just send it as a bearer token.
---
## 4. Call the API
Send the token in the `Authorization` header.
Smoke test — confirm your token resolves to the right account:
```bash
curl "https://api.psrestful.com/me" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
A normal read, using the same token:
```bash
curl "https://api.psrestful.com/v1.1.0/suppliers/PCNA/inventory-levels/..." \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
If `/me` returns your account, your OAuth2 setup is correct — any remaining `403`s are about
supplier credentialing, not your token. See [Troubleshooting](#7-troubleshooting).
---
## 5. Cache the token — don't mint one per request
Tokens are valid for 24 hours. **Fetch once, reuse until it's close to expiry**, then fetch a new
one. Minting a token on every API call is slow and unnecessary.
### Python
```python
API_BASE = "https://api.psrestful.com"
CLIENT_ID = "YOUR_CLIENT_ID"
CLIENT_SECRET = "YOUR_CLIENT_SECRET"
_token = {"value": None, "exp": 0}
def get_token() -> str:
# Refresh 60s before expiry to avoid edge-of-expiry failures.
if _token["value"] and time.time() < _token["exp"] - 60:
return _token["value"]
resp = requests.post(TOKEN_URL, json={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"audience": "psrestful",
"grant_type": "client_credentials",
}, timeout=15)
resp.raise_for_status()
data = resp.json()
_token["value"] = data["access_token"]
_token["exp"] = time.time() + data["expires_in"]
return _token["value"]
def get_inventory(supplier: str, product_id: str):
r = requests.get(
f"{API_BASE}/v1.1.0/suppliers/{supplier}/inventory-levels/{product_id}/",
headers={"Authorization": f"Bearer {get_token()}"},
params={"environment": "PROD"},
timeout=30,
)
r.raise_for_status()
return r.json()
```
### Node.js
```javascript
let cached = { value: null, exp: 0 };
async function getToken() {
if (cached.value && Date.now() / 1000 < cached.exp - 60) return cached.value;
const res = await fetch("https://auth.psrestful.com/oauth/token", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
client_id: process.env.PSRESTFUL_CLIENT_ID,
client_secret: process.env.PSRESTFUL_CLIENT_SECRET,
audience: "psrestful",
grant_type: "client_credentials",
}),
});
if (!res.ok) throw new Error(`token request failed: ${res.status}`);
const data = await res.json();
cached = { value: data.access_token, exp: Date.now() / 1000 + data.expires_in };
return cached.value;
}
```
---
## 6. Security best practices
- **Keep the secret server-side.** Never embed it in a browser, mobile app, or public repo. For
browser reads use the [public API key](/authentication) flow.
- **One app per integration.** Create a separate Developer App per system (ERP, storefront, staging).
You can then **rotate or delete** one without affecting the others.
- **Rotate on suspicion.** Use **Rotate** on the app's Settings tab if a secret may have leaked —
the old secret stops working immediately.
- **Store tokens in memory.** Treat access tokens like passwords; don't log them or write them to disk.
- **Always use HTTPS.**
---
## 7. Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `401 Unauthorized` | Token expired, missing, malformed, or `audience` ≠ `psrestful` when requested | Re-mint the token; confirm the `Authorization: Bearer` header and `audience=psrestful`. |
| `403 Forbidden` | Token is valid but the account has no credentials for that supplier, or the supplier rejected the upstream call | Add the supplier in the dashboard; verify your supplier account is active. `/me` should still succeed. |
| `403` on a PO or account endpoint with the **public** API key | The public (browser) key can't do writes or account operations | Use a **private** API key or an OAuth2 token. |
| `429 Too Many Requests` | You exceeded your plan's per-day quota | See [Rate Limits](/rate-limits); upgrade or spread out calls. |
The `/me` endpoint is the fastest way to isolate a problem: if it returns your account, your token
is good and the issue is downstream (supplier credentialing or rate limits).
---
## Related
- [Authentication](/authentication) — all auth methods (API key, public key, bearer token)
- [Roles & Permissions](/roles) — who can manage Developer Apps
- [Rate Limits](/rate-limits) — per-plan quotas and `429` handling
- [Credentials API](/credentials-api) — managing your supplier credentials
- [API Reference](https://api.psrestful.com/docs) — full endpoint catalog
---
## How to get order shipment notification
Source: https://docs.psrestful.com/guides/order-shipment-notification/how-to-get-order-shipment-notifications
## Function getOrderShipmentNotification
In order to get `order order shipment notification` for a given supplier we need to use Order Shipment Notification Service(OSN). Our API allows easy access by a `GET` request to our `order-shipment-notifications` endpoint.
An order can have shipment notifications once they reach the following statuses:
- Partially Shipped
- Shipped
- Complete
Hence, it doesn't make any sense to query by PO or SO search to orders in other statuses.
## Query Types
| query_type | Short Name | PSRESTful | Description |
|-------------|------------------|-----------|-----------------------------------------------------------------------------------------------------|
| 1 | PO Search | PO_SEARCH | Query based on customer provided purchase order number. |
| 2 | SO Search | SO_SEARCH | Query based on vendor assigned sales order number. |
| 3 | ShipDate Search | SHIP_DATE_SEARCH | Query based on all shipments with a shipment date greater than the value specified in `shipmentDateTimeStamp`. |
---
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/order-shipment-notifications/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-shipment-notifications/?query_type=PO_SEARCH&reference_number=50" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-shipment-notifications/",
params={"query_type": "PO_SEARCH", "reference_number": "50"},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-shipment-notifications/?query_type=PO_SEARCH&reference_number=50",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response:
This is the response from calling with:
`supplier_code` = `PCNA`
`query_type` = `PO_SEARCH`
`reference_number` = `50`
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-shipment-notifications/?query_type=PO_SEARCH&reference_number=50
```
## Error Response Example
```json copy
{
"OrderShipmentNotificationArray": null,
"ErrorMessage": {
"code": 301,
"description": "referenceNumber not found"
}
}
```
## Another Error Response Example
```json copy
{
"OrderShipmentNotificationArray": null,
"ErrorMessage": {
"code": 999,
"description": "Invalid SOAP params passed. The earliest date that can searched can be no longer than 7 days."
}
}
```
---
## How to get order status details
Source: https://docs.psrestful.com/guides/order-status/how-to-get-order-status-details
## Function getOrderStatusDetails
In order to get `order status detail` for a given supplier we need to use Order Status Service(ODRSTAT). Our API allows easy access by a `GET` request to our `order-status-details` endpoint.
## Query Types
| query_type | Short Name | PSRESTful | Description |
|-------------|------------------|-----------|----------------------------------------------------------------------------------------------|
| 1 | PO Search | PO_SEARCH | Query based on customer provided purchase order number. |
| 2 | SO Search | SO_SEARCH | Query based on vendor assigned sales order number. |
| 3 | Last Update Search | LAST_UPDATE_SEARCH | Query based on all orders with an update time greater than the value specified in `statusTimeStamp`. |
| 4 | All Open Search | ALL_OPEN_SEARCH | Query based on all orders that currently have a status other than “Complete” and “Cancelled”. |
You can use it in several ways. If you know the `Purchase Order` or `Sales Order` you can pass that
paremeter and also use the query type accordingly to `PO_SEARCH` or `SO_SEARCH`.
One very useful is to query by `ALL_OPEN_SEARCH`, in that case, the supplier will sending all the
open Orders.
Of course, you can also search using `LAST_UPDATE_SEARCH` in combination with `status_timestamp`.
In summary, `ALL_OPEN_SEARCH` and `LAST_UPDATE_SEARCH` can be used to prepare a CRON Job so you can keep your system updated.
`PO_SEARCH` and `SO_SEARCH` are useful to create a UI displaying the information for an order.
---
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/order-status-details/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-details/?query_type=ALL_OPEN_SEARCH" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-details/",
params={"query_type": "ALL_OPEN_SEARCH"},
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-details/?query_type=ALL_OPEN_SEARCH",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response:
This is the response from calling with:
`SUPPLIER_CODE` = `PCNA`
`query_type` = `ALL_OPEN_SEARCH`
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-details/?query_type=ALL_OPEN_SEARCH
```
```json copy
{
"OrderStatusArray": {
"OrderStatus": [
{
"purchaseOrderNumber": "PO043557",
"OrderStatusDetailArray": {
"OrderStatusDetail": [
{
"factoryOrderNumber": "35865767",
"statusID": 80,
"statusName": "Complete",
"responseRequired": null,
"validTimestamp": "2023-05-24T17:19:32.370000+00:00",
"expectedShipDate": "2023-05-24T23:59:00+00:00",
"expectedDeliveryDate": null,
"ResponseToArray": null,
"additionalExplanation": "Order has shipped in full—No further updates will be given"
}
]
}
},
{
"purchaseOrderNumber": "3900",
"OrderStatusDetailArray": {
"OrderStatusDetail": [
{
"factoryOrderNumber": "35900100",
"statusID": 80,
"statusName": "Complete",
"responseRequired": null,
"validTimestamp": "2023-07-03T21:19:02.887000+00:00",
"expectedShipDate": "2023-07-03T23:59:00+00:00",
"expectedDeliveryDate": null,
"ResponseToArray": null,
"additionalExplanation": "Order has shipped in full—No further updates will be given"
}
]
}
}
]
},
"ErrorMessage": null
}
```
---
## How to get order status types
Source: https://docs.psrestful.com/guides/order-status/how-to-get-order-status-types
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/order-status-types/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-types/" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-types/",
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-types/",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response:
This is the response from calling with:
`SUPPLIER_CODE` = `PCNA`
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/PCNA/order-status-types/
```
```json copy
{
"StatusArray": {
"Status": [
{
"id": 10,
"name": "Order Received"
},
{
"id": 20,
"name": "Order Confirmed"
},
{
"id": 42,
"name": "Proof Hold"
},
{
"id": 60,
"name": "In Production"
},
{
"id": 75,
"name": "Order has shipped in Part"
},
{
"id": 75,
"name": "Partial Shipment"
},
{
"id": 80,
"name": "Complete"
},
{
"id": 99,
"name": "Canceled"
}
]
},
"errorMessage": null
}
```
---
## Order Status API Guide
Source: https://docs.psrestful.com/guides/order-status/order-status-api-guide
# Order Status API Guide
## Overview
The Order Status service gives you near-real-time visibility into a purchase order's lifecycle from receipt through ship — without scraping inboxes or asking customer service for updates. It is the right tool to replace daily email/CSV reconciliation with a polling job that only pulls **what changed since last time**.
PSRESTful exposes the PromoStandards Order Status (`ODRSTAT`) service over HTTP/JSON. Two versions are available:
- **v1.0.0** — proven, broadly implemented, numeric `statusID`. Start here.
- **v2.0.0** — newer, richer payload (issues, products, contacts, `auditURL`), string status enum. Use when the supplier supports it and you want first-class issue tracking.
> **TL;DR for engineers:** *Do not loop `PO_SEARCH` across your order book.* Run one `LAST_UPDATE_SEARCH` call per supplier per polling tick and upsert the deltas. See [Recommended sync pattern](#6-recommended-sync-pattern-pseudo-code).
Spec: [Order Status 1.0.0](/standards/order-status-1.0.0) · [Order Status 2.0.0](/standards/order-status-2.0.0)
---
## 1. PromoStandards mapping
Every endpoint in this guide is a thin wrapper around an official PromoStandards method.
| REST endpoint | HTTP | PromoStandards method | Version |
|------------------------------------------------------------------------|------|-------------------------|---------|
| `/v1.0.0/suppliers/{supplier_code}/order-status-details` | GET | `getOrderStatusDetails` | 1.0.0 |
| `/v1.0.0/suppliers/{supplier_code}/order-status-types` | GET | `getOrderStatusTypes` | 1.0.0 |
| `/v2.0.0/suppliers/{supplier_code}/order-status` | GET | `getOrderStatus` | 2.0.0 |
| `/v2.0.0/suppliers/{supplier_code}/issues/{issue_id}` | GET | `getIssue` | 2.0.0 |
| `/v2.0.0/suppliers/{supplier_code}/service-methods` | GET | `getServiceMethods` | 2.0.0 |
Service code in our credentials/usage system: **`ODRSTAT`**.
---
## 2. Authentication & prerequisites
- **Auth**: API key (`X-API-Key`), OAuth2 Bearer, or HTTP Basic — all three work on these endpoints.
- **Supplier credentials**: Each supplier requires PromoStandards `ODRSTAT` credentials configured under your account before you can call the endpoints. Set them via the Credentials API.
- **Environment**: defaults to `PROD`. Pass `?environment=STAGING` to hit the supplier's sandbox.
- **Base URL**: `https://api.psrestful.com`
If credentials are missing or the supplier blocks your account you'll see `401`/`403` from the credential check before any SOAP call leaves our system.
---
## 3. Query types — pick the right one
`order-status-details` and `order-status` accept a required `query_type`:
| `query_type` | Meaning | `reference_number` | `status_timestamp` | Use when |
|----------------------|------------------------------------------------------|--------------------|--------------------|-----------------------------------|
| `PO_SEARCH` | Search by Purchase Order Number | required | — | You have a single, specific PO. |
| `SO_SEARCH` | Search by Sales (factory) Order Number | required | — | You have the supplier's SO number.|
| `ALL_OPEN_SEARCH` | All orders not in `Complete` / `Canceled` | — | — | **First-time backfill.** |
| `LAST_UPDATE_SEARCH` | Orders updated since `status_timestamp` (UTC) | — | required | **Incremental sync (recommended).** |
> ⚠️ **Anti-pattern:** Looping `PO_SEARCH` over your open-order list to "refresh statuses" will get you rate-limited, starve the supplier's SOAP backend, and the data is stale by the time you finish. Use `LAST_UPDATE_SEARCH`.
### Why `LAST_UPDATE_SEARCH` wins
A 4-row worked example for a typical merchant with 5,000 open orders across 4 suppliers, polling hourly:
| Strategy | Calls per hour | Calls per day | Likely outcome |
|----------------------------------------------------|---------------:|--------------:|---------------------------------------|
| `PO_SEARCH` per open order, sequentially | 5,000 | 120,000 | Throttled within minutes; never finishes. |
| `PO_SEARCH` per order, parallelized | 5,000 | 120,000 | Same total cost; supplier complaints. |
| `ALL_OPEN_SEARCH` per supplier | 4 | 96 | Works, but transfers the whole open book every tick. |
| **`LAST_UPDATE_SEARCH` per supplier** | **4** | **96** | **Only changed orders move; ~10–100 KB per call.** |
---
## 4. Endpoints
### 4.1 GET `/v1.0.0/suppliers/{supplier_code}/order-status-details`
Wraps PromoStandards **`getOrderStatusDetails`**.
**Path params**
| Name | Type | Description |
|-----------------|--------|----------------------------------------------|
| `supplier_code` | string | Supplier code (e.g., `GEM`, `HIT`, `PCNA`, `SanMar`). |
**Query params**
| Name | Type | Required | Description |
|--------------------|----------|----------|----------------------------------------------------------------|
| `query_type` | enum | yes | `PO_SEARCH` · `SO_SEARCH` · `LAST_UPDATE_SEARCH` · `ALL_OPEN_SEARCH` |
| `reference_number` | string | cond. | Required for `PO_SEARCH` / `SO_SEARCH`. |
| `status_timestamp` | datetime | cond. | Required for `LAST_UPDATE_SEARCH`. Send as ISO-8601 UTC. |
| `environment` | enum | no | `PROD` (default) · `STAGING`. |
**Example — incremental sync**
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/GEM/order-status-details?query_type=LAST_UPDATE_SEARCH&status_timestamp=2026-04-16T13:45:00Z" \
-H "X-API-Key: $PSRESTFUL_API_KEY"
```
**Response** (`200 OK`, abbreviated)
```json copy
{
"OrderStatusArray": {
"OrderStatus": [
{
"purchaseOrderNumber": "PO-100432",
"OrderStatusDetailArray": {
"OrderStatusDetail": [
{
"factoryOrderNumber": "GEM-77821",
"statusID": 60,
"statusName": "In Production",
"responseRequired": false,
"validTimestamp": "2026-04-16T17:02:11Z",
"expectedShipDate": "2026-04-22T00:00:00Z",
"expectedDeliveryDate": "2026-04-26T00:00:00Z",
"additionalExplanation": null
}
]
}
}
]
},
"errorMessage": null
}
```
**Errors** — `401`/`403` (credentials), `502`/`504` (supplier upstream), `429` (rate limit).
---
### 4.2 GET `/v1.0.0/suppliers/{supplier_code}/order-status-types`
Wraps PromoStandards **`getOrderStatusTypes`**. Returns the supplier's `{id, name}` status dictionary used to interpret `statusID` from `getOrderStatusDetails`.
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/GEM/order-status-types" \
-H "X-API-Key: $PSRESTFUL_API_KEY"
```
**Response** (`200 OK`)
```json copy
{
"StatusArray": {
"Status": [
{"id": 10, "name": "Order Received"},
{"id": 20, "name": "Order Confirmed"},
{"id": 60, "name": "In Production"},
{"id": 75, "name": "Partial Shipment"},
{"id": 80, "name": "Complete"}
]
}
}
```
> Resolve names dynamically via this endpoint instead of hard-coding the standard table — suppliers occasionally add custom IDs.
---
### 4.3 GET `/v2.0.0/suppliers/{supplier_code}/order-status`
Wraps PromoStandards **`getOrderStatus`** (v2.0.0). Same `query_type` semantics as v1.0.0, but each `OrderStatusDetail` may include `IssueArray`, `ProductArray`, `OrderContactArray`, and an `auditURL` for support escalation. `status` is now a string enum.
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/PCNA/order-status?query_type=LAST_UPDATE_SEARCH&status_timestamp=2026-04-16T13:45:00Z" \
-H "X-API-Key: $PSRESTFUL_API_KEY"
```
---
### 4.4 GET `/v2.0.0/suppliers/{supplier_code}/issues/{issue_id}`
Wraps PromoStandards **`getIssue`**. Fetches full detail for an order on hold.
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/PCNA/issues/ISS-2099" \
-H "X-API-Key: $PSRESTFUL_API_KEY"
```
`issueCategory` will be one of: `Order Entry Hold`, `General Hold`, `Credit Hold`, `Proof Hold`, `Art Hold`, `Back Order Hold`, `Shipping Hold`, `Customer Supplied Item Hold`. `issueStatus` is `Pending` · `Open` · `Closed`.
---
### 4.5 GET `/v2.0.0/suppliers/{supplier_code}/service-methods`
Wraps PromoStandards **`getServiceMethods`**. Discovery endpoint — returns which v2.0.0 methods this supplier actually implements. Call once per supplier on startup and cache the result.
---
## 5. Status reference (v1.0.0)
Standard `statusID` values defined by PromoStandards:
| ID | Name | Notes |
|----|-------------------|----------------------------------------|
| 10 | Order Received | Acknowledged but not yet entered. |
| 11 | Order Entry Hold | **Action needed.** |
| 20 | Order Confirmed | |
| 30 | Pre-Production | |
| 40 | General Hold | **Action needed.** |
| 41 | Credit Hold | **Action needed.** |
| 42 | Proof Hold | **Action needed.** |
| 43 | Art Hold | **Action needed.** |
| 44 | Back Order Hold | **Action needed.** |
| 60 | In Production | |
| 70 | In Storage | |
| 75 | Partial Shipment | Tracking lives in OSN — see §8. |
| 80 | Complete | Tracking lives in OSN — see §8. |
| 99 | Canceled | Terminal. |
In v2.0.0 the equivalent string enum is: `received`, `confirmed`, `preproduction`, `inProduction`, `inStorage`, `partiallyShipped`, `shipped`, `complete`, `canceled`.
---
## 6. Recommended sync pattern (pseudo-code)
The pattern below is the one we recommend for any merchant with more than a handful of open orders.
```python copy
# Conceptual sketch — adapt to your ORM / order store.
from datetime import datetime, timezone, timedelta
API = "https://api.psrestful.com"
API_KEY = ""
SUPPLIERS = ["GEM", "HIT", "PCNA", "SanMar"]
OVERLAP = timedelta(minutes=10) # absorb supplier clock skew
def last_synced_at(supplier_code: str) -> datetime | None:
"""Return cursor for this supplier, or None on first run."""
row = db.fetch_one(
"SELECT last_synced_at FROM order_sync_cursor WHERE supplier = :s",
{"s": supplier_code},
)
return row["last_synced_at"] if row else None
def save_cursor(supplier_code: str, ts: datetime) -> None:
db.execute(
"INSERT INTO order_sync_cursor(supplier, last_synced_at) VALUES (:s, :t) "
"ON CONFLICT (supplier) DO UPDATE SET last_synced_at = :t",
{"s": supplier_code, "t": ts},
)
def upsert_order_status(supplier_code: str, po: str, detail: dict) -> None:
"""
detail fields (v1.0.0): factoryOrderNumber, statusID, statusName, validTimestamp,
expectedShipDate, expectedDeliveryDate, responseRequired, additionalExplanation,
ResponseToArray.
"""
db.execute(
"""
INSERT INTO order_status (supplier, po, factory_order, status_id,
status_name, valid_ts, expected_ship,
expected_delivery, needs_response, note)
VALUES (:sup, :po, :fo, :sid, :sn, :vt, :es, :ed, :nr, :n)
ON CONFLICT (supplier, po, factory_order) DO UPDATE SET
status_id = EXCLUDED.status_id,
status_name = EXCLUDED.status_name,
valid_ts = EXCLUDED.valid_ts,
expected_ship = EXCLUDED.expected_ship,
expected_delivery = EXCLUDED.expected_delivery,
needs_response = EXCLUDED.needs_response,
note = EXCLUDED.note
WHERE EXCLUDED.valid_ts >= order_status.valid_ts
""",
{
"sup": supplier_code, "po": po,
"fo": detail["factoryOrderNumber"],
"sid": detail["statusID"],
"sn": detail.get("statusName"),
"vt": detail["validTimestamp"],
"es": detail.get("expectedShipDate"),
"ed": detail.get("expectedDeliveryDate"),
"nr": detail.get("responseRequired", False),
"n": detail.get("additionalExplanation"),
},
)
def sync_supplier(client: httpx.Client, supplier_code: str) -> None:
cursor = last_synced_at(supplier_code)
run_started = datetime.now(tz=timezone.utc)
if cursor is None:
# First run: backfill every currently-open order, then flip to incremental.
params = {"query_type": "ALL_OPEN_SEARCH"}
else:
params = {
"query_type": "LAST_UPDATE_SEARCH",
"status_timestamp": cursor.isoformat(),
}
r = client.get(
f"{API}/v1.0.0/suppliers/{supplier_code}/order-status-details",
params=params,
headers={"X-API-Key": API_KEY},
timeout=120,
)
r.raise_for_status()
payload = r.json()
orders = (payload.get("OrderStatusArray") or {}).get("OrderStatus") or []
for order in orders:
po = order["purchaseOrderNumber"]
for detail in order["OrderStatusDetailArray"]["OrderStatusDetail"]:
upsert_order_status(supplier_code, po, detail)
# Only advance cursor after a successful batch.
# Subtract OVERLAP so the next tick re-pulls a small overlapping window.
save_cursor(supplier_code, run_started - OVERLAP)
with httpx.Client() as client:
for code in SUPPLIERS:
try:
sync_supplier(client, code)
except Exception as e:
log.exception("order-status sync failed for %s: %s", code, e)
# Do NOT advance the cursor — next run re-tries the same window.
```
### Cursor-safety checklist
These four small things separate a sync that works on day one from one that works on day 90:
1. **Advance the cursor only on success.** A network blip or `502` from the supplier should leave the cursor where it was so the next tick re-tries.
2. **Idempotent upsert.** Key on `(supplier, purchaseOrderNumber, factoryOrderNumber)` and only overwrite when the incoming `validTimestamp` is newer. Retries and overlap windows then become safe.
3. **Always send UTC.** `status_timestamp` is timezone-sensitive and suppliers interpret naive datetimes inconsistently. Always send ISO-8601 with `Z` (or `+00:00`).
4. **Overlap window.** Subtract 5–10 minutes from `run_started` before saving the cursor to absorb clock skew between you, us, and the supplier. The idempotent upsert (#2) makes the duplicates harmless.
---
## 7. Polling cadence
| Order volume | Suggested cadence |
|---------------------|---------------------|
| High (1k+ open POs) | every **15 minutes** |
| Typical | every **hour** |
| Low / batch back-office | every **4–6 hours** |
Avoid sub-5-minute polls — most suppliers throttle ODRSTAT aggressively, and the underlying order state rarely changes that fast.
---
## 8. Handling held orders
Any v1.0.0 row whose `statusID` is one of `11, 40, 41, 42, 43, 44` (or any v2.0.0 row whose `issueCategory` is non-null) needs merchant action. Surface them in a dashboard rather than letting them rot.
- Check `responseRequired == true` and the `ResponseToArray` to find out who at the supplier you should email/call.
- On v2.0.0, follow `IssueArray[].issue_id` into `/v2.0.0/suppliers/{code}/issues/{issue_id}` for the full issue payload (history, notes, etc.).
- The v2.0.0 `auditURL` is a human-readable supplier link — surface it to your CS team for one-click escalation.
---
## 9. Capability probing (v2.0.0 only)
Before writing v2.0.0-specific code paths for a supplier, call `getServiceMethods` once on startup and cache the result. Some suppliers publish v2.0.0 but only implement a subset (e.g., `getOrderStatus` but not `getIssue`).
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/service-methods" \
-H "X-API-Key: $PSRESTFUL_API_KEY"
```
---
## 10. Cross-references
- **Tracking numbers** — once `statusID == 75` (Partial Shipment) or `80` (Complete), tracking numbers live in the **Order Shipment Notification** (`OSN`) service, not in `ODRSTAT`. Sync the two together.
- **Webhooks** — PromoStandards ODRSTAT is *pull-only*. There are no push notifications.
- **Spec reference** — [Order Status 1.0.0](/standards/order-status-1.0.0) · [Order Status 2.0.0](/standards/order-status-2.0.0)
---
## 11. v1.0.0 vs v2.0.0 — which should I use?
| Choose | When |
|---|---|
| **v1.0.0** | You're integrating a supplier that only publishes v1.0.0 (most still do), or you already key your downstream system off the numeric `statusID`. |
| **v2.0.0** | The supplier supports it (verify with `getServiceMethods`) and you want richer issue tracking, product detail, contact info, or the `auditURL`. |
A common pattern is to call v1.0.0 for the bulk sync and v2.0.0 only for the held-order subset where the richer issue payload pays for itself.
---
## 12. Error handling & rate limits
| Status | Meaning |
|----------|-----------------------------------------------------------|
| `200` | OK. |
| `401` | Missing or invalid PSRESTful credentials. |
| `403` | Authenticated but no access to this supplier's `ODRSTAT`. |
| `429` | Rate limit. Honor `Retry-After` and back off. |
| `502/504`| Supplier upstream timeout — retry with backoff. |
| `500` | Internal error. Safe to retry idempotently. |
Wrap each per-supplier sync in its own try/except so one supplier's outage never blocks the others.
---
**Service Code:** `ODRSTAT`
---
## How to get all available charges
Source: https://docs.psrestful.com/guides/ppc/how-to-get-all-available-charges
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/available-charges/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `localization_country` | string | No | Country code (default: `US`) |
| `localization_language` | string | No | Language code (default: `en`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/HIT/available-charges/55414/" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/available-charges/55414/",
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/available-charges/55414/",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `55414`
```json copy
{
"AvailableChargeArray": {
"AvailableCharge": [
{
"chargeId": 8046412,
"chargeName": "Pad Print",
"chargeType": "Setup",
"chargeDescription": "Pad Print"
},
{
"chargeId": 8046413,
"chargeName": "Laser Engrave",
"chargeType": "Setup",
"chargeDescription": "Laser Engrave"
},
{
"chargeId": 8046414,
"chargeName": "INDIVIDUAL PERSONALIZATION",
"chargeType": "Run",
"chargeDescription": "DECORATION"
},
{
"chargeId": 8046415,
"chargeName": "PERSONALIZATION",
"chargeType": "Setup",
"chargeDescription": "SETUP"
}
]
},
"errorMessage": null
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `AvailableChargeArray` | Array of available charges |
| `chargeId` | Unique identifier for the charge |
| `chargeName` | Name of the charge |
| `chargeType` | Type of charge: `Setup` or `Run` |
| `chargeDescription` | Description of the charge |
## Charge Types
| Type | Description |
|------|-------------|
| `Setup` | One-time charge per order (e.g., screen setup, plate creation) |
| `Run` | Per-unit charge (e.g., per-item decoration, personalization) |
## Common Charge Names
| Charge | Description |
|--------|-------------|
| Pad Print | Pad printing setup/run charge |
| Laser Engrave | Laser engraving setup/run charge |
| Screen Print | Screen printing setup/run charge |
| Embroidery | Embroidery setup/run charge |
| Rush | Rush order surcharge |
| Personalization | Individual item personalization |
## Use Cases
- **Quote generation**: Include all applicable charges in quotes
- **Order cost calculation**: Calculate total order cost including decoration charges
- **Customer communication**: Show customers what additional fees apply
## Related Guides
- [Get Available Locations](/guides/ppc/how-to-get-all-available-locations) - Get decoration locations
- [Get Decoration Colors](/guides/ppc/how-to-get-decoration-colors) - Get available decoration colors
- [Get Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration) - Get full pricing details
- [Get FOB Points](/guides/ppc/how-to-get-fob-points) - Get shipping origin points
---
## How to get all available locations
Source: https://docs.psrestful.com/guides/ppc/how-to-get-all-available-locations
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/available-locations/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `localization_country` | string | No | Country code (default: `US`) |
| `localization_language` | string | No | Language code (default: `en`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/HIT/available-locations/7746/" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/available-locations/7746/",
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/available-locations/7746/",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `7746`
```json copy
{
"AvailableLocationArray": {
"AvailableLocation": [
{
"locationId": 8,
"locationName": "FRONT"
},
{
"locationId": 9,
"locationName": "BACK"
}
]
},
"ErrorMessage": null
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `AvailableLocationArray` | Array of available decoration locations |
| `locationId` | Unique identifier for the location (used in other PPC calls) |
| `locationName` | Human-readable name of the location |
## Common Location Names
| Location | Description |
|----------|-------------|
| FRONT | Front of the product |
| BACK | Back of the product |
| LEFT SLEEVE | Left sleeve (apparel) |
| RIGHT SLEEVE | Right sleeve (apparel) |
| POCKET | Pocket area |
| WRAP | Full wrap around product |
## Use Cases
- **Decoration planning**: Determine where imprints can be placed on a product
- **Quote generation**: Know available locations before getting pricing
- **Order placement**: Use `locationId` when placing decorated orders
## Typical Workflow
1. Call this endpoint to get available locations for a product
2. Use `locationId` when calling [Get Decoration Colors](/guides/ppc/how-to-get-decoration-colors)
3. Use location information when getting [Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration)
## Related Guides
- [Get Decoration Colors](/guides/ppc/how-to-get-decoration-colors) - Get colors available at a location
- [Get Available Charges](/guides/ppc/how-to-get-all-available-charges) - Get decoration charges
- [Get Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration) - Get full pricing details
- [Get FOB Points](/guides/ppc/how-to-get-fob-points) - Get shipping origin points
---
## How to get decoration colors
Source: https://docs.psrestful.com/guides/ppc/how-to-get-decoration-colors
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/decoration-colors/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `location_id` | integer | Yes | Location ID (get from [Available Locations](/guides/ppc/how-to-get-all-available-locations)) |
| `decoration_id` | integer | No | Filter by specific decoration method |
| `localization_country` | string | No | Country code (default: `US`) |
| `localization_language` | string | No | Language code (default: `en`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/HIT/decoration-colors/55414/?location_id=53" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/decoration-colors/55414/",
headers={"X-API-Key": "your-api-key"},
params={"location_id": 53}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/decoration-colors/55414/?location_id=53",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `55414`
`location_id` = `53`
The response has been truncated to show 5 colors:
```json copy
{
"DecorationColors": {
"productId": "55414",
"locationId": "53",
"pmsMatch": true,
"fullColor": true,
"ColorArray": {
"Color": [
{
"colorName": "Athletic Gold",
"hex": null,
"approximatePms": null,
"standardColorName": null
},
{
"colorName": "Black",
"hex": null,
"approximatePms": null,
"standardColorName": null
},
{
"colorName": "Navy Blue",
"hex": null,
"approximatePms": null,
"standardColorName": null
},
{
"colorName": "Red",
"hex": null,
"approximatePms": null,
"standardColorName": null
},
{
"colorName": "White",
"hex": null,
"approximatePms": null,
"standardColorName": null
}
]
},
"DecorationMethodArray": {
"DecorationMethod": [
{
"decorationId": 3471228,
"decorationName": "Pad Print"
},
{
"decorationId": 3471229,
"decorationName": "Laser Engrave"
}
]
}
},
"ErrorMessage": null
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `productId` | Product identifier |
| `locationId` | Location identifier for the decoration |
| `pmsMatch` | Whether PMS color matching is available |
| `fullColor` | Whether full-color printing is available |
| `ColorArray` | Array of available colors |
| `DecorationMethodArray` | Array of decoration methods available at this location |
## Color Fields
| Field | Description |
|-------|-------------|
| `colorName` | Name of the color |
| `hex` | Hex color code (if available) |
| `approximatePms` | Approximate PMS color match |
| `standardColorName` | Standardized color name |
## Typical Workflow
1. Get [Available Locations](/guides/ppc/how-to-get-all-available-locations) to find decoration locations
2. Call this endpoint with the `location_id` to get available colors
3. Use color and decoration information when placing orders
## Use Cases
- **Design mockups**: Know what colors are available for decoration
- **Customer presentations**: Show available imprint colors
- **Order placement**: Specify valid colors when ordering
## Related Guides
- [Get Available Locations](/guides/ppc/how-to-get-all-available-locations) - Get decoration locations (required for `location_id`)
- [Get Available Charges](/guides/ppc/how-to-get-all-available-charges) - Get decoration charges
- [Get Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration) - Get full pricing details
- [Get FOB Points](/guides/ppc/how-to-get-fob-points) - Get shipping origin points
---
## How to get FOB points
Source: https://docs.psrestful.com/guides/ppc/how-to-get-fob-points
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/fob-points/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `localization_country` | string | No | Country code (default: `US`) |
| `localization_language` | string | No | Language code (default: `en`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/SS/fob-points/C1717/" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/SS/fob-points/C1717/",
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/SS/fob-points/C1717/",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `SS`
`PRODUCT_ID` = `C1717`
The response has been truncated to show 3 FOB points:
```json copy
{
"FobPointArray": {
"FobPoint": [
{
"fobId": "BX",
"fobPostalCode": "06610",
"fobCity": "BRIDGEPORT",
"fobState": "CT",
"fobCountry": "US",
"CurrencySupportedArray": {
"CurrencySupported": [
{
"currency": "USD"
}
]
},
"ProductArray": {
"Product": [
{
"productId": "C1717"
}
]
}
},
{
"fobId": "CC",
"fobPostalCode": "60490",
"fobCity": "Bolingbrook",
"fobState": "IL",
"fobCountry": "US",
"CurrencySupportedArray": {
"CurrencySupported": [
{
"currency": "USD"
}
]
},
"ProductArray": {
"Product": [
{
"productId": "C1717"
}
]
}
},
{
"fobId": "CN",
"fobPostalCode": "93725",
"fobCity": "FRESNO",
"fobState": "CA",
"fobCountry": "US",
"CurrencySupportedArray": {
"CurrencySupported": [
{
"currency": "USD"
}
]
},
"ProductArray": {
"Product": [
{
"productId": "C1717"
}
]
}
}
]
},
"ErrorMessage": null
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `FobPointArray` | Array of FOB (shipping origin) points |
| `fobId` | Unique identifier for the FOB point (used in pricing calls) |
| `fobPostalCode` | Postal code of the warehouse |
| `fobCity` | City of the warehouse |
| `fobState` | State/province of the warehouse |
| `fobCountry` | Country of the warehouse |
| `CurrencySupportedArray` | Currencies available at this location |
| `ProductArray` | Products available at this FOB point |
## Use Cases
- **Shipping optimization**: Choose the closest warehouse to reduce shipping costs
- **Pricing requests**: `fob_id` is required when calling [Get Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration)
- **Inventory availability**: Know which warehouses stock the product
## Typical Workflow
1. Call this endpoint to get available FOB points for a product
2. Select the appropriate `fobId` based on shipping destination
3. Use the `fobId` when calling [Get Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration)
## Related Guides
- [Get Pricing and Configuration](/guides/ppc/how-to-get-pricing-and-configuration) - Use `fob_id` to get pricing
- [Get Available Locations](/guides/ppc/how-to-get-all-available-locations) - Get decoration locations
- [Get Available Charges](/guides/ppc/how-to-get-all-available-charges) - Get additional charges
- [Get Decoration Colors](/guides/ppc/how-to-get-decoration-colors) - Get available decoration colors
---
## How to get configuration and pricing
Source: https://docs.psrestful.com/guides/ppc/how-to-get-pricing-and-configuration
## Function GetConfigurationAndPricing
In order to get configuration and pricing for a given product we need to use Product, Pricing and Configuration service (PPC). Our API allows easy access by a `GET` request to our `pricing-and-configuration` endpoint.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/pricing-and-configuration/{PRODUCT_ID}/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `currency` | string | Yes | Currency code (e.g., `USD`, `CAD`) |
| `fob_id` | string | Yes | FOB point ID (get from [FOB Points](/guides/ppc/how-to-get-fob-points)) |
| `price_type` | string | Yes | `Customer` or `List`. Customer pricing requires supplier credentials |
| `configuration_type` | string | No | `Blank` or `Decorated` (default: `Decorated`) |
| `part_id` | string | No | Filter pricing for a specific part/variant |
| `localization_country` | string | No | Country code (default: `US`) |
| `localization_language` | string | No | Language code (default: `en`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v1.0.0/suppliers/HIT/pricing-and-configuration/1035/?currency=USD&price_type=List&configuration_type=Decorated&fob_id=1" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/pricing-and-configuration/1035/",
headers={"X-API-Key": "your-api-key"},
params={
"currency": "USD",
"price_type": "List",
"configuration_type": "Decorated",
"fob_id": 1
}
)
data = response.json()
print(data)
```
```javascript copy
const params = new URLSearchParams({
currency: "USD",
price_type: "List",
configuration_type: "Decorated",
fob_id: "1"
});
const response = await fetch(
`https://api.psrestful.com/v1.0.0/suppliers/HIT/pricing-and-configuration/1035/?${params}`,
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `1035`
Response example:
```json copy
{
"ErrorMessage": null,
"Configuration": {
"PartArray": {
"Part": [
{
"partId": "1035BLK",
"partDescription": "BLACK",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035GRA",
"partDescription": "GRAY",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035GRK",
"partDescription": "KELLY GREEN",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035KHK",
"partDescription": "KHAKI",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035NAV",
"partDescription": "NAVY BLUE",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035ORN",
"partDescription": "ORANGE",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035PNK",
"partDescription": "PINK",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035RED",
"partDescription": "RED",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035ROY",
"partDescription": "ROYAL BLUE",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
},
{
"partId": "1035WHT",
"partDescription": "WHITE",
"PartPriceArray": null,
"partGroup": 1,
"nextPartGroup": null,
"partGroupRequired": true,
"partGroupDescription": "Part Group",
"ratio": "1.00",
"defaultPart": null,
"LocationIdArray": {
"LocationId": [
{
"locationId": 8
},
{
"locationId": 254
},
{
"locationId": 255
}
]
}
}
]
},
"LocationArray": {
"Location": [
{
"locationId": 8,
"locationName": "FRONT",
"DecorationArray": {
"Decoration": [
{
"decorationId": 1265947,
"decorationName": "TRANSFER",
"decorationGeometry": "Rectangle",
"decorationHeight": "1.8750",
"decorationWidth": "4.0000",
"decorationDiameter": null,
"decorationUom": "Inches",
"allowSubForDefaultLocation": false,
"allowSubForDefaultMethod": false,
"itemPartQuantityLTM": 0,
"ChargeArray": {
"Charge": [
{
"chargeId": 4389275,
"chargeName": "TRANSFER",
"chargeType": "Setup",
"chargeDescription": "TRANSFER",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Colors",
"price": "40.0000",
"discountCode": null,
"repeatPrice": "20.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2028-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 4389276,
"chargeName": "EXTRA COLOR CHARGE",
"chargeType": "Run",
"chargeDescription": "EXTRA COLOR CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 2,
"yUom": "Colors",
"price": "0.4000",
"discountCode": null,
"repeatPrice": "0.4000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 92125150,
"chargeName": "TRANSFER",
"chargeType": "Run",
"chargeDescription": "TRANSFER",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 144,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Colors",
"price": "1.2000",
"discountCode": null,
"repeatPrice": "1.2000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2023-03-31T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
}
]
},
"decorationUnitsIncluded": 0,
"decorationUnitsIncludedUom": "Colors",
"decorationUnitsMax": 4,
"defaultDecoration": false,
"leadTime": 3,
"rushLeadTime": 1
},
{
"decorationId": 1265948,
"decorationName": "EMBROIDERY",
"decorationGeometry": "Rectangle",
"decorationHeight": "2.2500",
"decorationWidth": "4.5000",
"decorationDiameter": null,
"decorationUom": "Inches",
"allowSubForDefaultLocation": false,
"allowSubForDefaultMethod": false,
"itemPartQuantityLTM": 0,
"ChargeArray": {
"Charge": [
{
"chargeId": 4389282,
"chargeName": "ADDITIONAL TAPE CHARGE",
"chargeType": "Setup",
"chargeDescription": "ADDITIONAL TAPE CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 7000,
"yUom": "Stitches",
"price": "28.0000",
"discountCode": null,
"repeatPrice": "28.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 4389283,
"chargeName": "EXTRA STITCHES CHARGE",
"chargeType": "Run",
"chargeDescription": "EXTRA STITCHES CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1000,
"yUom": "Stitches",
"price": "0.2800",
"discountCode": null,
"repeatPrice": "0.2800",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 4389284,
"chargeName": "METALLIC THREAD",
"chargeType": "Run",
"chargeDescription": "METALLIC THREAD",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Other",
"price": "0.2800",
"discountCode": null,
"repeatPrice": "0.2800",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 92125151,
"chargeName": "EMBROIDERY",
"chargeType": "Run",
"chargeDescription": "EMBROIDERY",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 144,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "1.8000",
"discountCode": null,
"repeatPrice": "1.8000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2023-03-31T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
}
]
},
"decorationUnitsIncluded": 0,
"decorationUnitsIncludedUom": "Stitches",
"decorationUnitsMax": 40000,
"defaultDecoration": true,
"leadTime": 3,
"rushLeadTime": 1
},
{
"decorationId": 1399509,
"decorationName": "3D EMBROIDERY",
"decorationGeometry": "Rectangle",
"decorationHeight": "2.2500",
"decorationWidth": "4.5000",
"decorationDiameter": null,
"decorationUom": "Inches",
"allowSubForDefaultLocation": false,
"allowSubForDefaultMethod": false,
"itemPartQuantityLTM": 0,
"ChargeArray": {
"Charge": [
{
"chargeId": 4389286,
"chargeName": "3D EMBROIDERY",
"chargeType": "Run",
"chargeDescription": "3D EMBROIDERY",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 144,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "2.9100",
"discountCode": null,
"repeatPrice": "2.9100",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 4389288,
"chargeName": "EXTRA STITCHES CHARGE",
"chargeType": "Run",
"chargeDescription": "EXTRA STITCHES CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1000,
"yUom": "Stitches",
"price": "0.4320",
"discountCode": null,
"repeatPrice": "0.4320",
"repeatDiscountCode": null,
"priceEffectiveDate": "2020-02-26T00:00:00",
"priceExpiryDate": "2030-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
}
]
},
"decorationUnitsIncluded": 0,
"decorationUnitsIncludedUom": "Stitches",
"decorationUnitsMax": 40000,
"defaultDecoration": false,
"leadTime": 3,
"rushLeadTime": 1
}
]
},
"decorationsIncluded": 0,
"defaultLocation": true,
"maxDecoration": 1,
"minDecoration": 0,
"locationRank": 1
},
{
"locationId": 254,
"locationName": "LEFT",
"DecorationArray": {
"Decoration": [
{
"decorationId": 459401177,
"decorationName": "EMBROIDERY",
"decorationGeometry": "Rectangle",
"decorationHeight": "2.0000",
"decorationWidth": "3.0000",
"decorationDiameter": null,
"decorationUom": "Inches",
"allowSubForDefaultLocation": false,
"allowSubForDefaultMethod": false,
"itemPartQuantityLTM": 0,
"ChargeArray": {
"Charge": [
{
"chargeId": 1469692412,
"chargeName": "EMBROIDERY",
"chargeType": "Run",
"chargeDescription": "EMBROIDERY",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 144,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "1.8000",
"discountCode": null,
"repeatPrice": "1.8000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469692933,
"chargeName": "TAPE CHARGE",
"chargeType": "Setup",
"chargeDescription": "TAPE CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "40.0000",
"discountCode": null,
"repeatPrice": "40.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469693588,
"chargeName": "EXTRA STITCHES CHARGE",
"chargeType": "Run",
"chargeDescription": "EXTRA STITCHES CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1000,
"yUom": "Stitches",
"price": "0.2800",
"discountCode": null,
"repeatPrice": "0.2800",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469693890,
"chargeName": "METALLIC THREAD",
"chargeType": "Run",
"chargeDescription": "METALLIC THREAD",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Other",
"price": "0.2800",
"discountCode": null,
"repeatPrice": "28.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469695029,
"chargeName": "ADDITIONAL TAPE CHARGE",
"chargeType": "Setup",
"chargeDescription": "ADDITIONAL TAPE CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 5000,
"yUom": "Stitches",
"price": "28.0000",
"discountCode": null,
"repeatPrice": "28.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1470291967,
"chargeName": "NAME",
"chargeType": "Run",
"chargeDescription": "NAME",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Other",
"price": "6.4000",
"discountCode": null,
"repeatPrice": "6.4000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1470292821,
"chargeName": "INITIALS",
"chargeType": "Run",
"chargeDescription": "INITIALS",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Other",
"price": "4.0000",
"discountCode": null,
"repeatPrice": "4.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
}
]
},
"decorationUnitsIncluded": 0,
"decorationUnitsIncludedUom": "Stitches",
"decorationUnitsMax": 40000,
"defaultDecoration": true,
"leadTime": 3,
"rushLeadTime": 1
}
]
},
"decorationsIncluded": 0,
"defaultLocation": false,
"maxDecoration": 1,
"minDecoration": 0,
"locationRank": 1
},
{
"locationId": 255,
"locationName": "RIGHT",
"DecorationArray": {
"Decoration": [
{
"decorationId": 459404742,
"decorationName": "EMBROIDERY",
"decorationGeometry": "Rectangle",
"decorationHeight": "2.0000",
"decorationWidth": "3.0000",
"decorationDiameter": null,
"decorationUom": "Inches",
"allowSubForDefaultLocation": false,
"allowSubForDefaultMethod": false,
"itemPartQuantityLTM": 0,
"ChargeArray": {
"Charge": [
{
"chargeId": 1469704832,
"chargeName": "EMBROIDERY",
"chargeType": "Run",
"chargeDescription": "EMBROIDERY",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 144,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "1.8000",
"discountCode": null,
"repeatPrice": "1.8000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469705028,
"chargeName": "TAPE CHARGE",
"chargeType": "Setup",
"chargeDescription": "TAPE CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "40.0000",
"discountCode": null,
"repeatPrice": "40.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469705383,
"chargeName": "ADDITIONAL TAPE CHARGE",
"chargeType": "Setup",
"chargeDescription": "ADDITIONAL TAPE CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 5000,
"yUom": "Stitches",
"price": "28.0000",
"discountCode": null,
"repeatPrice": "28.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469706166,
"chargeName": "EXTRA STITCHES CHARGE",
"chargeType": "Run",
"chargeDescription": "EXTRA STITCHES CHARGE",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1000,
"yUom": "Stitches",
"price": "0.2800",
"discountCode": null,
"repeatPrice": "0.2800",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1469706727,
"chargeName": "METALLIC THREAD",
"chargeType": "Run",
"chargeDescription": "METALLIC THREAD",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Stitches",
"price": "0.2800",
"discountCode": null,
"repeatPrice": "0.2800",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1470296870,
"chargeName": "NAME",
"chargeType": "Run",
"chargeDescription": "NAME",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Other",
"price": "6.4000",
"discountCode": null,
"repeatPrice": "6.4000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
},
{
"chargeId": 1470297467,
"chargeName": "INITIALS",
"chargeType": "Run",
"chargeDescription": "INITIALS",
"ChargePriceArray": {
"ChargePrice": [
{
"xMinQty": 1,
"xUom": "EA",
"yMinQty": 1,
"yUom": "Other",
"price": "4.0000",
"discountCode": null,
"repeatPrice": "4.0000",
"repeatDiscountCode": null,
"priceEffectiveDate": "2026-01-21T00:00:00",
"priceExpiryDate": "2032-01-01T00:00:00"
}
]
},
"chargesAppliesLTM": null,
"chargesPerLocation": null,
"chargesPerColor": null
}
]
},
"decorationUnitsIncluded": 0,
"decorationUnitsIncludedUom": "Stitches",
"decorationUnitsMax": 40000,
"defaultDecoration": true,
"leadTime": 3,
"rushLeadTime": 1
}
]
},
"decorationsIncluded": 0,
"defaultLocation": false,
"maxDecoration": 1,
"minDecoration": 0,
"locationRank": 1
}
]
},
"productId": "1035",
"currency": "USD",
"FobArray": {
"Fob": [
{
"fobId": "1",
"fobPostalCode": "33777"
},
{
"fobId": "12",
"fobPostalCode": "L6S6H2"
}
]
},
"fobPostalCode": null,
"priceType": "Net"
}
}
```
## Response Structure
| Field | Description |
|-------|-------------|
| `ErrorMessage` | Error details, `null` if successful |
| `Configuration` | Main configuration and pricing object |
| `Configuration.PartArray` | Array of available parts/variants (usually colors) |
| `Configuration.LocationArray` | Array of decoration locations with their methods and charges |
| `Configuration.productId` | The product ID queried |
| `Configuration.currency` | Currency used for pricing |
| `Configuration.FobArray` | Available FOB shipping points for this product |
| `Configuration.priceType` | The price type returned (e.g., `Net`, `List`) |
### Part Fields
| Field | Description |
|-------|-------------|
| `partId` | Unique identifier for the part/variant (e.g., `1035BLK`) |
| `partDescription` | Human-readable description (e.g., `BLACK`) |
| `PartPriceArray` | Per-part pricing, if available |
| `partGroup` | Group number for multi-step configuration |
| `nextPartGroup` | Next group in the configuration sequence |
| `partGroupRequired` | Whether selecting from this group is required |
| `partGroupDescription` | Description of the part group |
| `ratio` | Quantity ratio for this part |
| `defaultPart` | Whether this is the default selection |
| `LocationIdArray` | List of `locationId` values where this part can be decorated |
### Location Fields
| Field | Description |
|-------|-------------|
| `locationId` | Unique identifier for the decoration location |
| `locationName` | Name of the location (e.g., `FRONT`, `LEFT`, `RIGHT`) |
| `DecorationArray` | Available decoration methods at this location |
| `decorationsIncluded` | Number of decorations included in the base price |
| `defaultLocation` | Whether this is the default decoration location |
| `maxDecoration` | Maximum number of decorations allowed |
| `minDecoration` | Minimum number of decorations required |
| `locationRank` | Priority ranking of this location |
### Decoration Fields
| Field | Description |
|-------|-------------|
| `decorationId` | Unique identifier for the decoration method |
| `decorationName` | Name of the method (e.g., `TRANSFER`, `EMBROIDERY`, `3D EMBROIDERY`) |
| `decorationGeometry` | Shape of the imprint area (e.g., `Rectangle`) |
| `decorationHeight` | Height of the imprint area |
| `decorationWidth` | Width of the imprint area |
| `decorationDiameter` | Diameter (for circular imprint areas) |
| `decorationUom` | Unit of measure (e.g., `Inches`) |
| `ChargeArray` | Setup and run charges for this decoration |
| `decorationUnitsIncluded` | Number of units (colors/stitches) included in the base price |
| `decorationUnitsIncludedUom` | Unit of measure for included units (e.g., `Colors`, `Stitches`) |
| `decorationUnitsMax` | Maximum units allowed |
| `defaultDecoration` | Whether this is the default decoration method |
| `leadTime` | Standard lead time in days |
| `rushLeadTime` | Rush lead time in days |
### Charge Fields
| Field | Description |
|-------|-------------|
| `chargeId` | Unique identifier for the charge |
| `chargeName` | Name of the charge (e.g., `TRANSFER`, `EXTRA COLOR CHARGE`) |
| `chargeType` | `Setup` (one-time) or `Run` (per-unit) |
| `chargeDescription` | Description of the charge |
| `ChargePriceArray` | Price tiers for this charge |
### ChargePrice Fields
| Field | Description |
|-------|-------------|
| `xMinQty` | Minimum item quantity for this price tier |
| `xUom` | Unit of measure for quantity (e.g., `EA`) |
| `yMinQty` | Minimum units for the second dimension (colors, stitches, etc.) |
| `yUom` | Unit for the second dimension (e.g., `Colors`, `Stitches`) |
| `price` | Price per unit |
| `repeatPrice` | Price for repeat/reorder |
| `priceEffectiveDate` | Date this pricing takes effect |
| `priceExpiryDate` | Date this pricing expires |
## Price Types
| Type | Description |
|------|-------------|
| `List` | Standard list pricing (no credentials required) |
| `Customer` | Your negotiated pricing (requires supplier credentials) |
## Configuration Types
| Type | Description |
|------|-------------|
| `Blank` | Pricing for undecorated products |
| `Decorated` | Pricing including decoration/imprint |
## Typical Workflow
1. Get [FOB Points](/guides/ppc/how-to-get-fob-points) to find available shipping origins
2. Get [Available Locations](/guides/ppc/how-to-get-all-available-locations) to find decoration locations
3. Get [Available Charges](/guides/ppc/how-to-get-all-available-charges) to understand additional fees
4. Call this endpoint with the required parameters to get pricing
## Related Guides
- [Get FOB Points](/guides/ppc/how-to-get-fob-points) - Get shipping origin points (required for `fob_id`)
- [Get Available Locations](/guides/ppc/how-to-get-all-available-locations) - Get decoration locations
- [Get Available Charges](/guides/ppc/how-to-get-all-available-charges) - Get additional charges
- [Get Decoration Colors](/guides/ppc/how-to-get-decoration-colors) - Get available decoration colors
---
## How to get all sellable products
Source: https://docs.psrestful.com/guides/product-data/how-to-get-all-sellable-products
We enable the direct call to this method using `sellable-products` path although we don't recommend this API call.
We have created two API operations to help you getting what you need but faster.
`sellable-product-ids` and `sellables`. They are similar, underliying we call the same SOAP operation but they both offers a different response.
Because, they have been cached, these two options are better.
Let's dive in.
docs: [https://api.psrestful.com/docs/Product Data](https://api.psrestful.com/docs/#Product%20Data)
## Function getProductSellable
In order to get `all sellable products` for a given product we need to use Product Data service(Product). Our API allows easy access by a `GET` request to our `sellable-products` endpoint.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/sellable-products/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/sellable-products/" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v2.0.0/suppliers/HIT/sellable-products/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with no parameter. The response has been truncated because it was too large.
```json copy
{
"ProductSellableArray": {
"ProductSellable": [
{
"productId": "1035",
"partId": "1035ATHGLD",
"culturePoint": null
},
{
"productId": "1035",
"partId": "1035BLK",
"culturePoint": null
},
{
"productId": "1035",
"partId": "1035GRA",
"culturePoint": null
},
{
"productId": "1035",
"partId": "1035GRK",
"culturePoint": null
},
{
"productId": "1035",
"partId": "1035KHK",
"culturePoint": null
},
{
"productId": "1035",
"partId": "1035NAV",
"culturePoint": null
},
...
]
},
"ServiceMessageArray": null
}
```
## Sellable Product Ids
In most cases, this is the API you need to use to get all the products that the supplier is able to sell.
A call to `/v{api_version}/suppliers/{supplier_code}/sellable-product-ids` will return a simpler JSON object
with the following format:
```json copy
{
"count": 149,
"products": [
"KEPH",
"TUSCANYBT",
"IMPERIALW",
...
]
}
```
With this API you will be able to loop through all the product ids regardless if you want to update your catalogue, update your inventory, etc.
It is a lot faster than the original because it doesn't have part information. In the PromoStandards version, Product information is getting dupplicated making some responses several megabytes long.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/sellable-product-ids/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/Protowels/sellable-product-ids/" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v2.0.0/suppliers/Protowels/sellable-product-ids/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with no parameter. The response has been truncated because it was too large.
```json copy
{
"count": 149,
"products": [
"KEPH",
"TUSCANYBT",
"IMPERIALW",
"DRI-16L",
"SHOREBUDDY",
"BT18",
"TL-19",
"PESH",
"SUB-2242",
"SPABP",
"SC-35",
"CH5060"
...
]
}
```
## Sellables
If you want also to know all the variants a product have without calling `getProduct` you can use `sellables` API.
`/v{api_version}/suppliers/{supplier_code}/sellables`
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/sellables/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/sellables/" \
-H "X-API-Key: your-api-key"
```
```python copy
headers = {"X-API-Key": "your-api-key"}
response = requests.get(url, headers=headers)
data = response.json()
print(data)
```
```javascript copy
const url = "https://api.psrestful.com/v2.0.0/suppliers/HIT/sellables/";
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": "your-api-key",
},
});
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with no parameter. The response has been truncated because it was too large.
```json copy
{
"count": 4163,
"products": [
{
"productId": "1035",
"variants": [
"1035ROY",
"1035NAV",
"1035GRA",
"1035ORN",
"1035ATHGLD",
"1035PNK",
"1035BLK",
"1035WHT",
"1035GRK",
"1035KHK",
"1035",
"1035GRN",
"1035RED"
],
"no_variants": 13
},
{
"productId": "2799",
"variants": [
"2799ORNORN",
"2799BLUWHT",
"2799REDRED",
"2799BLKBLK",
"2799BLKWHT",
"2799",
"2799REDWHT",
"2799WHTWHT",
"2799BLUBLU"
],
"no_variants": 9
},
...
}
```
## Related Guides
- [How to get product details](/guides/product-data/how-to-get-product) - Get detailed information for a specific product
- [How to get products modified since a date](/guides/product-data/how-to-get-products-modified-since) - Track product changes over time
- [How to get closeout products](/guides/product-data/how-to-get-closeout-products) - Find discontinued products
---
## How to get closeout products
Source: https://docs.psrestful.com/guides/product-data/how-to-get-closeout-products
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/products-closeout/
### Query Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `environment` | string | PROD | Environment: `PROD` or `STAGING` |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/products-closeout/" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products-closeout/",
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products-closeout/",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v2.0.0/suppliers/HIT/products-closeout/
```
```json copy
{
"ProductCloseOutArray": {
"ProductCloseOut": [
{
"productId": "4521",
"partId": "4521BLK"
},
{
"productId": "4521",
"partId": "4521BLU"
},
{
"productId": "6234",
"partId": "6234RED"
}
]
},
"ServiceMessageArray": null
}
```
## Response Structure
The response contains a `ProductCloseOutArray` with `ProductCloseOut` items, each containing:
| Field | Description |
|-------|-------------|
| `productId` | The product identifier |
| `partId` | The specific part/variant identifier that is on closeout |
## Use Cases
- **Bargain hunting**: Find discounted products for price-sensitive customers
- **Inventory clearance**: Help customers find deals before products become unavailable
- **Catalog updates**: Identify products to remove or mark as closeout in your catalog
- **Purchasing decisions**: Stock up on popular closeout items before they're gone
## API Versions
Both v1.0.0 and v2.0.0 are supported:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/products-closeout/
https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/products-closeout/
```
## Related Guides
- [How to get all sellable products](/guides/product-data/how-to-get-all-sellable-products) - Get the full list of sellable products
- [How to get product details](/guides/product-data/how-to-get-product) - Get detailed information for a specific product
- [How to get products modified since a date](/guides/product-data/how-to-get-products-modified-since) - Track product changes over time
---
## How to get product details
Source: https://docs.psrestful.com/guides/product-data/how-to-get-product
## Function getProduct
In order to get `all product details` for a given product we need to use Product Data service(Product). Our API allows easy access by a `GET` request to our `product` endpoint.
### HTTP VERB: `GET`
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/products/{PRODUCT_ID}/
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/" \
-H "X-API-Key: your-api-key"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
{
headers: {
"X-API-Key": "your-api-key"
}
}
);
const data = await response.json();
console.log(data);
```
### Example Response
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
`PRODUCT_ID` = `5989`
```json copy
{
"Product": {
"productId": "5989",
"productName": "32 Oz. Tritan™ Hydrator Sports Bottle",
"description": [
"Durable Tritan™ Material. Impact And Shatter Resistant. Self-Attached, Screw On, Spill-Resistant Lid. Wide Mouth Opening. Smooth Rubberized Finish. Meets FDA Requirements. BPA Free. Hand Wash Recommended."
],
"priceExpiresDate": null,
"productBrand": "TRITAN",
"export": true,
"lastChangeDate": "2020-12-11T15:08:55",
"creationDate": "2018-01-15T13:56:31",
"endDate": "2028-01-01T00:00:00",
"effectiveDate": "2018-01-15T13:56:31",
"isCaution": false,
"cautionComment": null,
"isCloseout": false,
"lineName": "HIT",
"primaryImageURL": null,
"complianceInfoAvailable": null,
"unspscCommodityCode": null,
"imprintSize": "Width - 3.0000 Width 3.0000 and height 4.0000 shape Rectangle and uom INCH",
"defaultSetUpCharge": null,
"defaultRunCharge": null,
"ProductCategoryArray": {
"ProductCategory": [
{
"category": "Drinkware",
"subCategory": "Tritan™ Series"
},
{
"category": "Drinkware",
"subCategory": "Bottles - Plastic"
}
]
},
"RelatedProductArray": {
"RelatedProduct": [
{
"relationType": "Common Grouping",
"productId": "5835",
"partId": null
},
{
"relationType": "Common Grouping",
"productId": "5807",
"partId": null
}
]
},
"ProductPartArray": {
"ProductPart": [
{
"partId": "5989BLK",
"description": [],
"countryOfOrigin": "CN",
"primaryMaterial": "Tritan",
"shape": "BOTTLE",
"ApparelSize": null,
"Dimension": {
"dimensionUom": "IN",
"depth": null,
"height": 8.5,
"width": null,
"weightUom": "LB",
"weight": 0.42
},
"leadTime": 5,
"unspsc": null,
"gtin": null,
"isRushService": false,
"endDate": "2028-01-01T00:00:00",
"effectiveDate": "2018-01-15T13:56:31",
"isCloseout": false,
"isCaution": null,
"cautionComment": null,
"nmfcCode": 0.0,
"nmfcDescription": null,
"nmfcNumber": null,
"isOnDemand": false,
"isHazmat": null,
"primaryColor": null,
"ColorArray": {
"Color": [
{
"colorName": "BLACK",
"hex": "000000",
"approximatePms": "Black",
"standardColorName": null
}
]
},
"ProductPackagingArray": null,
"ShippingPackageArray": {
"ShippingPackage": [
{
"packageType": "Box",
"description": "WC",
"quantity": 24.0,
"dimensionUom": "IN",
"depth": 16.0,
"height": 23.0,
"width": 11.0,
"weightUom": "LB",
"weight": 10.0
}
]
}
},
{
"partId": "5989BLU",
"description": [],
"countryOfOrigin": "CN",
"primaryMaterial": "Tritan",
"shape": "BOTTLE",
"ApparelSize": null,
"Dimension": {
"dimensionUom": "IN",
"depth": null,
"height": 8.5,
"width": null,
"weightUom": "LB",
"weight": 0.42
},
"leadTime": 5,
"unspsc": null,
"gtin": null,
"isRushService": false,
"endDate": "2028-01-01T00:00:00",
"effectiveDate": "2018-01-15T13:56:31",
"isCloseout": false,
"isCaution": null,
"cautionComment": null,
"nmfcCode": 0.0,
"nmfcDescription": null,
"nmfcNumber": null,
"isOnDemand": false,
"isHazmat": null,
"primaryColor": null,
"ColorArray": {
"Color": [
{
"colorName": "BLUE",
"hex": "0047BB",
"approximatePms": "2728",
"standardColorName": null
}
]
},
"ProductPackagingArray": null,
"ShippingPackageArray": {
"ShippingPackage": [
{
"packageType": "Box",
"description": "WC",
"quantity": 24.0,
"dimensionUom": "IN",
"depth": 16.0,
"height": 23.0,
"width": 11.0,
"weightUom": "LB",
"weight": 10.0
}
]
}
},
{
"partId": "5989LIM",
"description": [],
"countryOfOrigin": "CN",
"primaryMaterial": "Tritan",
"shape": "BOTTLE",
"ApparelSize": null,
"Dimension": {
"dimensionUom": "IN",
"depth": null,
"height": 8.5,
"width": null,
"weightUom": "LB",
"weight": 0.42
},
"leadTime": 5,
"unspsc": null,
"gtin": null,
"isRushService": false,
"endDate": "2028-01-01T00:00:00",
"effectiveDate": "2018-01-15T13:56:31",
"isCloseout": false,
"isCaution": null,
"cautionComment": null,
"nmfcCode": 0.0,
"nmfcDescription": null,
"nmfcNumber": null,
"isOnDemand": false,
"isHazmat": null,
"primaryColor": null,
"ColorArray": {
"Color": [
{
"colorName": "LIME",
"hex": "97D700",
"approximatePms": "375",
"standardColorName": null
}
]
},
"ProductPackagingArray": null,
"ShippingPackageArray": {
"ShippingPackage": [
{
"packageType": "Box",
"description": "WC",
"quantity": 24.0,
"dimensionUom": "IN",
"depth": 16.0,
"height": 23.0,
"width": 11.0,
"weightUom": "LB",
"weight": 10.0
}
]
}
},
{
"partId": "5989RED",
"description": [],
"countryOfOrigin": "CN",
"primaryMaterial": "Tritan",
"shape": "BOTTLE",
"ApparelSize": null,
"Dimension": {
"dimensionUom": "IN",
"depth": null,
"height": 8.5,
"width": null,
"weightUom": "LB",
"weight": 0.42
},
"leadTime": 5,
"unspsc": null,
"gtin": null,
"isRushService": false,
"endDate": "2028-01-01T00:00:00",
"effectiveDate": "2018-01-15T13:56:31",
"isCloseout": false,
"isCaution": null,
"cautionComment": null,
"nmfcCode": 0.0,
"nmfcDescription": null,
"nmfcNumber": null,
"isOnDemand": false,
"isHazmat": null,
"primaryColor": null,
"ColorArray": {
"Color": [
{
"colorName": "RED",
"hex": "C8102E",
"approximatePms": "186",
"standardColorName": null
}
]
},
"ProductPackagingArray": null,
"ShippingPackageArray": {
"ShippingPackage": [
{
"packageType": "Box",
"description": "WC",
"quantity": 24.0,
"dimensionUom": "IN",
"depth": 16.0,
"height": 23.0,
"width": 11.0,
"weightUom": "LB",
"weight": 10.0
}
]
}
}
]
},
"ProductKeywordArray": null,
"LocationDecorationArray": {
"LocationDecoration": [
{
"locationName": "SIDE1",
"maxImprintColors": null,
"decorationName": "Silk Screen",
"locationDecorationComboDefault": false,
"priceIncludes": false
}
]
},
"ProductPriceGroupArray": {
"ProductPriceGroup": [
{
"groupName": "Standard Imprint",
"currency": "USD",
"description": null,
"ProductPriceArray": {
"ProductPrice": [
{
"quantityMax": null,
"quantityMin": 48,
"price": 11.98,
"discountCode": "C"
},
{
"quantityMax": null,
"quantityMin": 96,
"price": 10.42,
"discountCode": "C"
}
]
}
}
]
},
"FobPointArray": {
"FobPoint": [
{
"fobId": "1",
"fobPostalCode": "33777",
"fobCity": "Largo",
"fobState": "FL",
"fobCountry": "US"
},
{
"fobId": "12",
"fobPostalCode": "L6S6H2",
"fobCity": "BRAMPTON",
"fobState": "ON",
"fobCountry": "CA"
}
]
},
"ProductMarketingPointArray": {
"ProductMarketingPoint": [
{
"pointType": "Highlight",
"pointCopy": "Durable Tritan™ Material."
},
{
"pointType": "Highlight",
"pointCopy": "Impact And Shatter Resistant."
}
]
}
},
"ServiceMessageArray": null
}
```
## Example of a product not available
```json copy
{
"Product": null,
"ServiceMessageArray": {
"ServiceMessage": [
{
"code": 400,
"description": "Product Id is unavailable for sale.",
"severity": "Error"
}
]
}
}
```
## Related Guides
- [How to get all sellable products](/guides/product-data/how-to-get-all-sellable-products) - Get the full list of sellable products
- [How to get products modified since a date](/guides/product-data/how-to-get-products-modified-since) - Track product changes over time
- [How to get closeout products](/guides/product-data/how-to-get-closeout-products) - Find discontinued products
---
## How to get products modified since a date
Source: https://docs.psrestful.com/guides/product-data/how-to-get-products-modified-since
### URL: https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/products-modified-since/
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `since` | datetime | Yes | ISO 8601 datetime (e.g., `2024-01-15T00:00:00`) |
| `environment` | string | No | Environment: `PROD` or `STAGING` (default: `PROD`) |
### Example Request
```bash copy
curl -X GET "https://api.psrestful.com/v2.0.0/suppliers/HIT/products-modified-since/?since=2024-12-01T00:00:00" \
-H "X-API-Key: your-api-key"
```
```python copy
)
print(response.json())
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products-modified-since/?since=2024-12-01T00:00:00",
{
headers: { "X-API-Key": "your-api-key" }
}
);
const data = await response.json();
console.log(data);
```
### Example Response
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v2.0.0/suppliers/HIT/products-modified-since/?since=2024-12-01T00:00:00
```
```json copy
{
"ProductDateModifiedArray": {
"ProductDateModified": [
{
"productId": "5989"
},
{
"productId": "6234"
},
{
"productId": "7891"
},
{
"productId": "4521"
}
]
},
"ServiceMessageArray": null
}
```
## Response Structure
The response contains a `ProductDateModifiedArray` with `ProductDateModified` items:
| Field | Description |
|-------|-------------|
| `productId` | The product identifier that has been modified since the specified date |
## Typical Workflow
1. **Initial sync**: Fetch all products using [sellable-product-ids](/guides/product-data/how-to-get-all-sellable-products#sellable-product-ids)
2. **Store last sync time**: Record when you last synchronized
3. **Incremental sync**: Use `products-modified-since` with your last sync time
4. **Update changed products**: Fetch full details only for modified products using [getProduct](/guides/product-data/how-to-get-product)
```python copy
# Example incremental sync workflow
from datetime import datetime, timedelta
# Your last sync timestamp
last_sync = datetime(2024, 12, 1, 0, 0, 0)
# Get modified products
response = requests.get(
f"https://api.psrestful.com/v2.0.0/suppliers/HIT/products-modified-since/",
params={"since": last_sync.isoformat()},
headers={"X-API-Key": "your-api-key"}
)
modified_products = response.json()["ProductDateModifiedArray"]["ProductDateModified"]
# Update only changed products
for product in modified_products:
product_id = product["productId"]
# Fetch and update this product in your catalog
update_product_in_catalog(product_id)
# Update your last sync time
last_sync = datetime.now()
```
```javascript copy
// Example incremental sync workflow
const lastSync = "2024-12-01T00:00:00";
// Get modified products
const response = await fetch(
`https://api.psrestful.com/v2.0.0/suppliers/HIT/products-modified-since/?since=${lastSync}`,
{
headers: { "X-API-Key": "your-api-key" }
}
);
const result = await response.json();
const modifiedProducts = result.ProductDateModifiedArray.ProductDateModified;
// Update only changed products
for (const product of modifiedProducts) {
const productId = product.productId;
// Fetch and update this product in your catalog
await updateProductInCatalog(productId);
}
```
## Use Cases
- **Catalog synchronization**: Keep your product catalog in sync without full refreshes
- **Price monitoring**: Detect products with pricing changes
- **Inventory systems**: Trigger inventory checks for updated products
- **Change detection**: Build audit logs of product modifications
## API Versions
Both v1.0.0 and v2.0.0 are supported:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/products-modified-since/?since=2024-12-01T00:00:00
https://api.psrestful.com/v2.0.0/suppliers/{SUPPLIER_CODE}/products-modified-since/?since=2024-12-01T00:00:00
```
## Best Practices
1. **Store timestamps**: Always store your last successful sync timestamp
2. **Use reasonable intervals**: Don't query with dates too far in the past; for large date ranges, consider a full sync
3. **Handle empty responses**: If no products have changed, the array will be empty
4. **Combine with other endpoints**: After getting modified IDs, use [getProduct](/guides/product-data/how-to-get-product) for full details
## Related Guides
- [How to get all sellable products](/guides/product-data/how-to-get-all-sellable-products) - For initial catalog sync
- [How to get product details](/guides/product-data/how-to-get-product) - Get detailed information for modified products
- [How to get closeout products](/guides/product-data/how-to-get-closeout-products) - Find discontinued products
---
## How to import products into your backend
Source: https://docs.psrestful.com/guides/product-data/how-to-import-products-to-backend
2. **Hydrate**: for every `extraId`, call `GET /extra/v2/products/{extra_id}` with `expand=medias&expand=combined_ppc&expand=inventory&expand=classifications`. A single response now contains everything needed to build a full catalog entry: descriptive fields, variants, images, pricing, stock, and third-party classifications.
This guide stops at the point where you have a fully hydrated product in memory. The **mapping** into your backend's schema and the **translation layer** for categories, locations, and decorations are your responsibility, covered in [Step 4](#step-4-map-to-your-backend-schema) and the [translation layer](#things-that-need-a-translation-layer) callout.
---
## Prerequisites
- A PSRESTful API key. See [Authentication](/authentication).
- Knowledge of your merchant backend's product, variant, image, price, and inventory schemas.
- (Optional, Python only) **[psdomain](https://github.com/GallardoSolutions/psdomain)**: Pydantic models for every PSRESTful/PromoStandards payload so you can work with typed objects instead of dicts. Requires Python 3.12+.
---
## Step 1. Discover the products to import
List products with whatever filters match your import policy. The most common starting point is "all active products for one supplier":
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products?status=active&supplier_code=HIT&page_size=100" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/extra/v2/products?status=active&supplier_code=HIT&page_size=100",
{ headers: { "X-API-Key": "your-api-key" } }
);
const data = await response.json();
```
### Useful filters for import scenarios
| Parameter | Type | Description |
|-----------|------|-------------|
| `status` | string | `draft`, `active`, `closeout`, `discontinued` |
| `supplier_code` | string | Import one supplier at a time (e.g. `HIT`, `SanMar`) |
| `brand` | integer | Filter to a single brand |
| `main_category` | string | Supplier's top-level category |
| `is_rush_service` | boolean | Only products with rush service |
| `lead_time__range` | string | e.g. `5,10` for 5 to 10 day lead times |
| `list_price__range` | string | e.g. `0,25` for items priced ≤ $25 |
| `search` | string | Matches name, productId, part SKUs, GTINs |
| `ordering` | string | `name`, `list_price`, `-lead_time`, etc. |
### Walk the pagination
Save each result's `extraId`; you'll use it in Step 2.
```python copy
def list_all_products(api_key: str, supplier_code: str):
url = "https://api.psrestful.com/extra/v2/products"
params = {"status": "active", "supplier_code": supplier_code, "page_size": 100, "page": 1}
headers = {"X-API-Key": api_key}
while True:
resp = requests.get(url, headers=headers, params=params).json()
for item in resp["results"]:
yield item["extraId"]
if resp.get("next") is None:
break
params["page"] += 1
```
---
## Step 2. Hydrate with full detail via `expand`
Call the detail endpoint for each `extraId` and request every expansion you need in a single round-trip:
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/products/21832?expand=medias&expand=combined_ppc&expand=inventory&expand=classifications¤cy=USD" \
-H "X-API-Key: your-api-key"
```
```python copy
)
data = response.json()
```
```javascript copy
const url = new URL("https://api.psrestful.com/extra/v2/products/21832");
["medias", "combined_ppc", "inventory", "classifications"].forEach(v =>
url.searchParams.append("expand", v)
);
url.searchParams.set("currency", "USD");
const response = await fetch(url, { headers: { "X-API-Key": "your-api-key" } });
const data = await response.json();
```
### Expansion options
| Expansion | What you get | Use for |
|-----------|--------------|---------|
| `medias` | Images, art templates, and videos per product / part | Product gallery, color swatches, decoration previews |
| `combined_ppc` | List / net / customer pricing plus decoration pricing | Price lists, customer-specific pricing |
| `inventory` | Stock quantity per FOB point / warehouse | Availability, future available when supplier supports INV 2.0.0 |
| `classifications` | Google, Shopify categories | SEO, marketplace feeds, tax configuration |
With all four expansions the response contains everything required for a complete catalog entry in one request.
---
## Step 3. Use `psdomain` instead of raw dicts (Python)
[psdomain](https://github.com/GallardoSolutions/psdomain) is the Pydantic model library for PSRESTful/PromoStandards payloads. It replaces hand-written dict access (`data["Product"]["ProductPartArray"]["ProductPart"][0]["ColorArray"]["Color"][0]["hex"]`) with typed attribute access and validation.
Install:
```bash copy
pip install git+https://github.com/GallardoSolutions/psdomain.git
```
Key models relevant to an import flow:
| Payload | Model | Import |
|---------|-------|--------|
| Standard Product Data 2.0.0 response | `ProductResponseV200` | `from psdomain.model.product_data.v_2_0_0 import ProductResponseV200` |
| Media Content | `MediaContent` | `from psdomain.model.media_content import MediaContent` |
| Configuration & Pricing | `ConfigurationAndPricingResponse` | `from psdomain.model.ppc import ConfigurationAndPricingResponse` |
| Inventory 2.0.0 | `InventoryLevelsResponseV200` | `from psdomain.model.inventory.v_2_0_0 import InventoryLevelsResponseV200` |
Parse a Product Data response:
```python copy
from psdomain.model.product_data.v_2_0_0 import ProductResponseV200
resp = ProductResponseV200.model_validate({
"Product": product_payload, # from PSRESTful
"ServiceMessageArray": None,
})
product = resp.Product
print(product.productId, product.productName)
print("Colors:", [c.colorName for c in product.available_colors])
print("Sizes:", product.sizes)
for part in product.ProductPartArray.ProductPart:
print(part.partId, part.ColorArray.Color[0].hex if part.ColorArray else None)
```
`Product` exposes convenience properties such as `available_colors`, `sizes`, and `variants_per_color`, which remove most of the boilerplate from an importer.
---
## Step 4. Map to your backend schema
The hydrated payload carries far more information than most backends model natively. Pick the fields that matter for your catalog and drop the rest.
| PSRESTful field | Typical target field | Notes |
|-----------------|----------------------|-------|
| `supplierCode` + `productId` | SKU or external reference | `productId` is not globally unique; always qualify it with `supplierCode` |
| `productName` | Title | |
| `description` (list of strings) | Body / long description | Join with `\n\n` |
| `productBrand`, `lineName` | Brand / product line | |
| `primaryImageURL` | Hero image | Fall back to the first `medias` entry |
| `ProductPartArray[].partId` + color + size | Variant SKU | One row per part |
| `ColorArray[].hex` | Variant swatch | Prefer `hex` over `colorName` |
| `medias` (from expansion) | Product gallery | Filter by `mediaType=PRIMARY_IMAGE` for the hero |
| `combined_ppc` (from expansion) | Price tiers | Choose list / net / customer per your pricing policy |
| `inventory` (from expansion) | Stock level | Sum across FOB points or store per-warehouse |
| `FobPointArray` | Ship-from origin | Useful for freight estimates |
| `classifications.google` (from expansion) | Google Product Category | For Google Shopping / Merchant Center feeds |
### Things that need a translation layer
These fields **cannot** be imported as-is. Every supplier uses its own vocabulary, and every merchant backend has its own. Build a translation layer and keep it under version control.
- **Categories**: `ProductCategoryArray` and `mainCategory` use the supplier's taxonomy, which rarely matches yours. Maintain a mapping table (`supplier_code`, `supplier_category` → `your_category_id`) and fail-open to a default (e.g. `Uncategorized`) for unseen values. Review unmapped categories periodically.
- **Decoration locations**: `LocationDecoration.locationName` values (`SIDE1`, `FRONT`, `LC`, `LEFT CHEST`, …) vary by supplier. Map to your standard location vocabulary.
- **Decoration methods**: `LocationDecoration.decorationName` (`Silk Screen` vs. `Screen Print` vs. `SP`) needs normalization.
- **Colors**: prefer `Color.hex` for visual matching; `colorName` and `approximatePms` differ across suppliers, and `standardColorName` is often `null`.
- **Sizes**: apparel parts expose `ApparelSize` (with `labelSize`, `apparelStyle`, `sizeGroup`); hard goods do not. Your importer must handle both shapes.
- **Units of measure**: `Dimension.dimensionUom` and `weightUom` mix `IN`/`CM` and `LB`/`KG` across suppliers. Convert to a canonical unit on import.
---
## Step 5. Keep the import fresh
A one-shot import is rarely enough. For ongoing sync:
- **Product changes**: use [How to get products modified since](/guides/product-data/how-to-get-products-modified-since) to pull only products that changed since your last run.
- **Closeouts**: [How to get closeout products](/guides/product-data/how-to-get-closeout-products) lets you flag items for retirement.
- **Inventory-only refresh**: hitting the detail endpoint for every SKU is overkill when you only need stock. Use the cached inventory endpoint:
```bash copy
curl -X GET "https://api.psrestful.com/extra/v2/inventory/HIT?last_modified__since=1h&page_size=500" \
-H "X-API-Key: your-api-key"
```
See [Extra APIs: Inventory](/extra-apis#inventory-api) for all filters.
---
## Putting it together
A minimal, backend-agnostic importer. The `upsert_product(...)` function is where your backend-specific code lives: BigCommerce REST calls, an Odoo RPC client, SQLAlchemy models, a CSV writer, whatever you need.
```python copy
from psdomain.model.product_data.v_2_0_0 import ProductResponseV200
API_ROOT = "https://api.psrestful.com"
HEADERS = {"X-API-Key": "your-api-key"}
EXPAND = [("expand", "medias"), ("expand", "combined_ppc"),
("expand", "inventory"), ("expand", "classifications")]
def iter_extra_ids(supplier_code: str):
params = {"status": "active", "supplier_code": supplier_code,
"page_size": 100, "page": 1}
while True:
page = requests.get(f"{API_ROOT}/extra/v2/products",
headers=HEADERS, params=params).json()
for row in page["results"]:
yield row["extraId"]
if page.get("next") is None:
break
params["page"] += 1
def fetch_detail(extra_id: int) -> dict:
return requests.get(
f"{API_ROOT}/extra/v2/products/{extra_id}",
headers=HEADERS,
params=EXPAND + [("currency", "USD")],
).json()
def upsert_product(detail: dict) -> None:
"""Implement for your backend (BigCommerce, Odoo, Postgres, ...)."""
resp = ProductResponseV200.model_validate(
{"Product": detail, "ServiceMessageArray": None}
)
product = resp.Product
pricing = detail.get("combinedPpc")
inventory = detail.get("inventory")
media = detail.get("medias")
# ...map into your backend and write.
def run(supplier_code: str) -> None:
for extra_id in iter_extra_ids(supplier_code):
upsert_product(fetch_detail(extra_id))
if __name__ == "__main__":
run("HIT")
```
---
## Related guides
- [Get All Sellable Products](/guides/product-data/how-to-get-all-sellable-products)
- [Get Products Modified Since](/guides/product-data/how-to-get-products-modified-since)
- [Get Closeout Products](/guides/product-data/how-to-get-closeout-products)
- [Get Product Details](/guides/product-data/how-to-get-product)
- [Extra APIs reference](/extra-apis)
---
## How to place a Purchase Order for blank goods
Source: https://docs.psrestful.com/guides/purchase-order/send-po/place-blank-order
# Function SendPO
In order to send a purchase order for blank goods we need to use Purchase Order Service(PO). Our API allows easy access by a `POST` request to our `purchase-orders` endpoint.
You need to use authentication in order to use this service. More information on how to authenticate can be found [here](/authentication/).
### HTTP VERB: `POST`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/purchase-orders/
### Example Response:
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
### Payload
```json copy
{
"orderType": "Blank",
"orderNumber": "59",
"orderDate": "2023-06-15T17:47:26.102Z",
"lastModified": "2023-06-15T17:47:26.102Z",
"totalAmount": 309.60,
"paymentTerms": "NET60",
"rush": false,
"currency": "USD",
"DigitalProof": null,
"OrderContactArray": {
"Contact": [
{
"contactType": "Order",
"ContactDetails": {
"attentionTo": "John Doe",
"companyName": "My Company",
"address1": "Address 1",
"address2": "Address 2",
"city": "City",
"region": "State",
"postalCode": "zip code",
"country": "US",
"email": "me@mycompany.com",
"phone": "",
"comments": ""
},
"accountName": null,
"accountNumber": null
}
]
},
"ShipmentArray": {
"Shipment": [
{
"ShipTo": {
"customerPickup": false,
"shipmentId": 0,
"ContactDetails": {
"attentionTo": "John Doe",
"companyName": "My Company",
"address1": "Address 1",
"address2": "Address 2",
"city": "City",
"region": "State",
"postalCode": "Zip Code",
"country": "US",
"email": "me@mycompany.com",
"phone": "",
"comments": ""
}
},
"packingListRequired": true,
"blindShip": true,
"allowConsolidation": true,
"FreightDetails": {
"carrier": "UPS",
"service": "Ground"
},
"ThirdPartyAccount": null,
"shipReferences": null,
"comments": "string"
}
]
},
"LineItemArray": {
"LineItem": [
{
"lineNumber": "1",
"description": "10 units of 2811BLK",
"lineType": "New",
"Quantity": {
"value": 100,
"uom": "EA"
},
"fobId": "1",
"ToleranceDetails": {
"tolerance": "ExactOnly",
"value": 0,
"uom": "Percent"
},
"allowPartialShipments": false,
"unitPrice": 3.096,
"lineItemTotal": 309.60,
"requestedShipDate": "2023-06-15",
"requestedInHandsDate": "2023-06-25",
"referenceSalesQuote": null,
"Program": null,
"endCustomerSalesOrder": "4343",
"productId": "30070",
"customerProductId": "1996",
"lineItemGroupingId": null,
"PartArray": {
"Part": [
{
"partId": "30070BLK",
"Quantity": {
"value": 100,
"uom": "EA"
},
"partGroup": "",
"customerPartId": "",
"customerSupplied": true,
"description": "",
"locationLinkId": [
0
],
"unitPrice": 3.096,
"extendedPrice": 309.60,
"ShipmentLinkArray": {
"ShipmentLink": [
{
"shipmentId": 1,
"Quantity": {
"value": 100,
"uom": "EA"
}
}
]
}
}
]
},
"Configuration": null
}
]
},
"termsAndConditions": "terms agreed to",
"salesChannel": null,
"promoCode": null,
"TaxInformationArray": null
}
```
### Sending the Request
```bash copy
curl -X POST "https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d @payload.json
```
```python copy
import requests
payload = { ... } # Use the payload structure above
response = requests.post(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/",
json=payload,
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const payload = { ... }; // Use the payload structure above
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/",
{
method: "POST",
headers: {
"X-API-Key": "your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
const data = await response.json();
console.log(data);
```
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/
```
## Response
```json copy
{
"transactionId": "115867",
"ServiceMessageArray": {
"ServiceMessage": [
{
"code": 999,
"description": "General Error - Contact the System Service Provider Details: **WARNING* Fix: Multiple Models for XXX, must confirm",
"severity": "Warning"
}
]
}
}
```
---
## How to place a Purchase Order for a configured order
Source: https://docs.psrestful.com/guides/purchase-order/send-po/place-configured-order
# Function SendPO
In order to send a purchase order for `Configured` orders we need to use Purchase Order Service(PO). Our API allows easy access by a `POST` request to our `send_po` endpoint. Very similar to blank goods but the configuration type of the order should change.
You need to use authentication in order to use this service. More information on how to authenticate can be found [here](/authentication/).
### HTTP VERB: `POST`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/purchase-orders/
### Example Response:
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
### Payload
```json copy
{
"environment": "PROD",
"orderType": "Configured",
"orderNumber": "BHF1",
"orderDate": "2024-03-28T00:17:41.015Z",
"lastModified": "2024-03-28T00:17:41.015Z",
"totalAmount": 0,
"paymentTerms": "NET30",
"rush": false,
"currency": "USD",
"DigitalProof": {
"required": true,
"DigitalProofAddressArray": {
"DigitalProofAddress": [
{
"type": "Email",
"email": "email@example.com",
"lineItemGroupingId": 0
}
]
}
},
"OrderContactArray": {
"Contact": [
{
"contactType": "Art",
"ContactDetails": {
"attentionTo": "[Name]",
"companyName": "[Company Name]",
"address1": "[Address1]",
"city": "",
"region": "[FL]",
"postalCode": "",
"country": "US",
"email": "email@example.com",
"phone": "phone number"
},
"accountName": "Gallardo Solutions Corp",
"accountNumber": ""
},
{
"contactType": "Order",
"ContactDetails": {
"attentionTo": "Name",
"companyName": "Name",
"address1": "",
"city": "",
"region": "GA",
"postalCode": "",
"country": "US",
"email": "",
"phone": "",
"comments": ""
},
"accountName": "Gallardo Solutions Corp",
"accountNumber": ""
}
]
},
"ShipmentArray": {
"Shipment": [
{
"ShipTo": {
"customerPickup": true,
"shipmentId": 0,
"ContactDetails": {
"attentionTo": "[NAME]",
"companyName": "[COMPANY NAME]",
"address1": "[ADDRESS1]",
"address2": "",
"address3": "",
"city": "[CITY]",
"region": "[STATE]",
"postalCode": "[POSTAL CODE]",
"country": "US",
"email": "[EMAIL]",
"phone": "[PHONE]",
"comments": ""
}
},
"packingListRequired": true,
"blindShip": true,
"allowConsolidation": true,
"FreightDetails": {
"carrier": "UPS",
"service": "Ground"
},
"ThirdPartyAccount": null
}
]
},
"LineItemArray": {
"LineItem": [
{
"referenceSalesQuote": null,
"Program": null,
"endCustomerSalesOrder": null,
"lineNumber": "1",
"description": "Whistle Light/Key Chain",
"lineType": "New",
"Quantity": {
"value": 250,
"uom": "EA"
},
"ToleranceDetails": {
"tolerance": "AllowOverRun",
"value": 0,
"uom": "Percent"
},
"allowPartialShipments": true,
"unitPrice": 0.726,
"lineItemTotal": 181.5,
"requestedShipDate": "2024-03-28",
"requestedInHandsDate": "2024-03-28",
"productId": "72",
"customerProductId": "72",
"lineItemGroupingId": 0,
"PartArray": {
"Part": [
{
"partGroup": null,
"partId": "0072PUR",
"Quantity": {
"value": 250,
"uom": "EA"
},
"unitPrice": 0,
"extendedPrice": 0,
"customerPartId": "0072PUR",
"customerSupplied": false,
"description": "Whistle Light/Key Chain",
"locationLinkId": [1],
"ShipmentLinkArray": {
"ShipmentLink": [
{
"shipmentId": 0,
"Quantity": {
"value": 250,
"uom": "EA"
}
}
]
}
}
]
},
"Configuration": {
"referenceNumber": "",
"referenceNumberType": "PurchaseOrder",
"preProductionProof": false,
"ChargeArray": {
"Charge": [
{
"chargeId": "4830907",
"chargeName": "PAD PRINT",
"description": "PAD PRINT",
"chargeType": "Setup",
"Quantity": {
"value": 1,
"uom": "EA"
},
"unitPrice": 0,
"extendedPrice": 0
}
]
},
"LocationArray": {
"Location": [
{
"locationLinkId": 1,
"locationId": 53,
"locationName": "SIDE1",
"DecorationArray": {
"Decoration": [
{
"decorationId": 819101,
"decorationName": "PAD PRINT",
"Artwork": {
"instructions": "WHITE COLOR",
"Layers": null,
"TypesetArray": null,
"totalStitchCount": null,
"refArtworkId": "",
"description": "",
"Dimensions": {
"geometry": "Rectangle",
"useMaxLocationDimensions": true,
"height": 0,
"width": 0,
"diameter": 0,
"uom": "INCH"
},
"ArtworkFileArray": {
"ArtworkFile": [
{
"fileName": "logo",
"fileLocation": "https://xxx.s3.amazonaws.com/xxx+logo.ai",
"transportMechanism": "Url",
"artworkType": "ProductionReady"
}
]
}
}
}
]
}
}
]
}
}
}
]
},
"termsAndConditions": "terms agreed to",
"salesChannel": "API",
"TaxInformationArray": null
}
```
### Sending the Request
```bash copy
curl -X POST "https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d @payload.json
```
```python copy
import requests
payload = { ... } # Use the payload structure above
response = requests.post(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/",
json=payload,
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const payload = { ... }; // Use the payload structure above
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/",
{
method: "POST",
headers: {
"X-API-Key": "your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
const data = await response.json();
console.log(data);
```
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/
```
## Response
```json copy
{
"transactionId": "115867",
"ServiceMessageArray": null
}
```
---
## How to place a Purchase Order for a sample order
Source: https://docs.psrestful.com/guides/purchase-order/send-po/place-sample-order
# Function SendPO
In order to send a purchase order for `sample` goods we need to use Purchase Order Service(PO). Our API allows easy access by a `POST` request to our `send_po` endpoint. Very similar to blank goods but the configuration type of the order should change.
You need to use authentication in order to use this service. More information on how to authenticate can be found [here](/authentication/).
### HTTP VERB: `POST`
### URL: https://api.psrestful.com/v1.0.0/suppliers/{SUPPLIER_CODE}/purchase-orders/
### Example Response:
This is the response from calling with:
`SUPPLIER_CODE` = `HIT`
### Payload
```json copy
{
"orderType": "Sample",
"orderNumber": "59",
"orderDate": "2023-06-15T17:47:26.102Z",
"lastModified": "2023-06-15T17:47:26.102Z",
"totalAmount": 309.60,
"paymentTerms": "NET60",
"rush": false,
"currency": "USD",
"DigitalProof": null,
"OrderContactArray": {
"Contact": [
{
"contactType": "Order",
"ContactDetails": {
"attentionTo": "John Doe",
"companyName": "My Company",
"address1": "Address 1",
"address2": "Address 2",
"city": "City",
"region": "State",
"postalCode": "zip code",
"country": "US",
"email": "me@mycompany.com",
"phone": "",
"comments": ""
},
"accountName": null,
"accountNumber": null
}
]
},
"ShipmentArray": {
"Shipment": [
{
"ShipTo": {
"customerPickup": false,
"shipmentId": 0,
"ContactDetails": {
"attentionTo": "John Doe",
"companyName": "My Company",
"address1": "Address 1",
"address2": "Address 2",
"city": "City",
"region": "State",
"postalCode": "Zip Code",
"country": "US",
"email": "me@mycompany.com",
"phone": "",
"comments": ""
}
},
"packingListRequired": true,
"blindShip": true,
"allowConsolidation": true,
"FreightDetails": {
"carrier": "UPS",
"service": "Ground"
},
"ThirdPartyAccount": null,
"shipReferences": null,
"comments": "string"
}
]
},
"LineItemArray": {
"LineItem": [
{
"lineNumber": "1",
"description": "10 units of 2811BLK",
"lineType": "New",
"Quantity": {
"value": 100,
"uom": "EA"
},
"fobId": "1",
"ToleranceDetails": {
"tolerance": "ExactOnly",
"value": 0,
"uom": "Percent"
},
"allowPartialShipments": false,
"unitPrice": 3.096,
"lineItemTotal": 309.60,
"requestedShipDate": "2023-06-15",
"requestedInHandsDate": "2023-06-25",
"referenceSalesQuote": null,
"Program": null,
"endCustomerSalesOrder": "4343",
"productId": "30070",
"customerProductId": "1996",
"lineItemGroupingId": null,
"PartArray": {
"Part": [
{
"partId": "30070BLK",
"Quantity": {
"value": 100,
"uom": "EA"
},
"partGroup": "",
"customerPartId": "",
"customerSupplied": true,
"description": "",
"locationLinkId": [
0
],
"unitPrice": 3.096,
"extendedPrice": 309.60,
"ShipmentLinkArray": {
"ShipmentLink": [
{
"shipmentId": 1,
"Quantity": {
"value": 100,
"uom": "EA"
}
}
]
}
}
]
},
"Configuration": null
}
]
},
"termsAndConditions": "terms agreed to",
"salesChannel": null,
"promoCode": null,
"TaxInformationArray": null
}
```
### Sending the Request
```bash copy
curl -X POST "https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d @payload.json
```
```python copy
import requests
payload = { ... } # Use the payload structure above
response = requests.post(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/",
json=payload,
headers={"X-API-Key": "your-api-key"}
)
data = response.json()
print(data)
```
```javascript copy
const payload = { ... }; // Use the payload structure above
const response = await fetch(
"https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/",
{
method: "POST",
headers: {
"X-API-Key": "your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
const data = await response.json();
console.log(data);
```
**COMPLETE URL**:
```url copy
https://api.psrestful.com/v1.0.0/suppliers/HIT/purchase-orders/
```
## Response
```json copy
{
"transactionId": "115867",
"ServiceMessageArray": {
"ServiceMessage": [
{
"code": 999,
"description": "General Error - Contact the System Service Provider Details: **WARNING* Fix: Multiple Models for XXX, must confirm",
"severity": "Warning"
}
]
}
}
```
---
## Understanding Shipment & Shipping Fields
Source: https://docs.psrestful.com/guides/purchase-order/shipping-fields
# Understanding Shipment & Shipping Fields
The `Shipment` object in a Purchase Order controls where and how goods are shipped. This guide explains each shipping-related field and how they work together.
For the full schema reference, see the [official PromoStandards Purchase Order spec](/standards/purchase-order-1.0.0).
## Who Handles Shipping?
There are three main scenarios for who handles shipping:
| Scenario | customerPickup | FreightDetails | ThirdPartyAccount |
|---------------------------------------|----------------|----------------|--------------------|
| Distributor picks up from supplier | `true` | not needed | not needed |
| Supplier ships (supplier's account) | `false` | provided | `null` |
| Supplier ships (distributor's account)| `false` | provided | provided |
### customerPickup
When `true`, the distributor arranges their own pickup from the supplier's warehouse. No carrier or freight information is needed in this case.
### FreightDetails
Specifies how the shipment should be sent:
- **`carrier`** — The shipping vendor name (e.g. `"UPS"`, `"FEDEX"`). Optional.
- **`service`** — The service level code (e.g. `"GROUND"`, `"2DAY"`, `"NEXTDAY"`). Optional.
Both fields are optional per the SOAP spec (`minOccurs="0"`); some suppliers may only need the carrier.
### ThirdPartyAccount
When provided, the supplier bills shipping to the distributor's own carrier account. It contains:
- **`accountName`** — Name on the carrier account
- **`accountNumber`** — The carrier account number
- **`ContactDetails`** — Contact information for the paying entity
When `null`, the supplier uses their own shipping account.
## Shipment Behavior Fields
### blindShip
When `true`, the supplier ships without revealing their identity to the end customer. The packing slip and label show the distributor's info (or no supplier branding). This is standard practice for most distributors to protect their supplier relationships.
### packingListRequired
When `true`, the supplier must include a packing list with the shipment. The packing list typically itemizes the contents of the package.
### allowConsolidation
When `true`, the supplier may combine this shipment with other shipments heading to the same destination. This can reduce shipping costs but may delay delivery if the supplier waits to consolidate. When `false`, this shipment must be sent independently.
## JSON Examples
### Supplier ships with their account (most common)
```json
{
"Shipment": {
"customerPickup": false,
"ShipTo": {
"customerName": "Acme Corp",
"address1": "123 Main St",
"city": "Anytown",
"state": "CA",
"postalCode": "90210",
"country": "US"
},
"FreightDetails": {
"carrier": "UPS",
"service": "GROUND"
},
"ThirdPartyAccount": null,
"blindShip": true,
"packingListRequired": true,
"allowConsolidation": false
}
}
```
### Distributor's own carrier account
```json
{
"Shipment": {
"customerPickup": false,
"ShipTo": {
"customerName": "Acme Corp",
"address1": "123 Main St",
"city": "Anytown",
"state": "CA",
"postalCode": "90210",
"country": "US"
},
"FreightDetails": {
"carrier": "FEDEX",
"service": "2DAY"
},
"ThirdPartyAccount": {
"accountName": "Acme Distribution",
"accountNumber": "123456789",
"ContactDetails": {
"attentionTo": "Shipping Dept",
"address1": "456 Warehouse Blvd",
"city": "Commerce",
"state": "CA",
"postalCode": "90040",
"country": "US"
}
},
"blindShip": true,
"packingListRequired": true,
"allowConsolidation": false
}
}
```
### Customer pickup
```json
{
"Shipment": {
"customerPickup": true,
"ShipTo": {
"customerName": "Acme Corp",
"address1": "123 Main St",
"city": "Anytown",
"state": "CA",
"postalCode": "90210",
"country": "US"
},
"blindShip": false,
"packingListRequired": false,
"allowConsolidation": false
}
}
```
---
## Inventory 1.2.1
Source: https://docs.psrestful.com/standards/inventory-1.2.1
# **PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES**

# Inventory Interface Standards
Version: 1.2.1
Date: 14-Apr-2015
Document Change Log
| Version | Date | Reason for Change | Author |
| ------- | ---------- | ----------------- | ------ |
| .1 | 2014-2-10 | Initial Draft | Eric Shonebarger, CIO Hit Promotional Products |
| .2 | 2014-02-19 | Review by BIC Graphic personnel:
Removed authentication standard in lieu of all parties using following their standards.
Added ProductIDType to Request:
Added companion inventory to Reply:
Added multiple array detail fields | Tim Nale, Director IT BIC Graphic |
| .3 | 2014-03-03 | Modified Custom Attributes to ARRAY Added reference to Data Mining / Abuse | Tim Nale, Director IT BIC Graphic |
| 1.0 | 2014-03-27 | Modified Quantity Available data type; added version control | Tim Nale, Director IT BIC Graphic |
| 1.0.0 | 2014-04-28 | Updated casing on field names and Array Naming convention | Jon Norris, System Operations Manager Starline |
| 1.2.0 | 2015-3-24 | Added Color, Size, and Generic Selection Criteria. Added controlled list of Error Codes | Tim Nale, Director IT BIC Graphic |
| 1.2.1 | 2015-3-31 | Correct getFilterValue Request and Reply for proper naming of uniqueness | Tim Nale, Director IT BIC Graphic |
| 1.2.1 | 2015-4-14 | Added entryType to ProductCompanionInventory documentation. This was already in WSDL just excluded from documentation.
Modified error code 115 to include "not"
Added error code 120: generic way to handle required fields
Added error code 125: generic way to handle "not supported" error messages
Corrected typo in field name to match WSDL in "GetFiltervaluesReply" from "filterSelectionsArray" to "filterSelectionArray"
Corrected typo in field name to match WSDL in "Request" from "filterSelectionsArray" to "filterSelectionArray" | Jon Norris, VP Operations, Starline |
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface. Additionally, this document will provide sample code in order to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at [http://www.w3.org/TR/soap12-part1/](http://www.w3.org/TR/soap12-part1/)
Previous Versions
Inventory 1.0.0 - replaced 04/01/2015:
[View Documentation](https://tools.promostandards.org/9-webservice-documentation/7-inventory?tmpl=component)
## Inventory
There will be two functions available as part of this web service.
- getFilterValues()
- getInventoryLevels()
### Function: getFilterValues()
This function is optional and can be called to get the List of Values that can be passed to the getInventoryLevels function to limit the result set.
### GetFilterValuesRequest Object
| Field | Description | Data Type | Required? |
| ------------- | -------------------------------------------------------- | --------------------- | --------- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING: Enumerated | TRUE |
| id | The customerID or any other agreed upon ID. | 64 STRING | TRUE |
| password | The password associated with the customerID. | 64 STRING | FALSE |
| productID | Item's ID | 64 STRING | TRUE |
| productIDtype | productID type (Distributor, Supplier) | 64 STRING | TRUE |
### GetFilterValuesReply Object
| Field | Description | Data Type | Required? |
| -------------------- | ----------- | --------- | --------- |
| productID | The associated product. | 64 STRING | TRUE |
| filterColorArray | An array of different selections the product is offered and can be provided as a filter to Inventory Service getInventoryLevels. | 256 STRING ARRAY | FALSE |
| filterSizeArray | An array of different selections the product is offered and can be provided as a filter to Inventory Service getInventoryLevels. | 256 STRING ARRAY | FALSE |
| filterSelectionArray | An array of different selections other than Color and Size the product is offered and can be provided as a filter to Inventory Service getInventoryLevels. | 256 STRING ARRAY | FALSE |
| errorMessage | Response for any error requiring notification to requestor | 256 STRING: Enumerated | FALSE |
### Function: getInventoryLevels()
This function will provide the results of the current Inventory Levels for the product enquired filter by the criteria provided.
### Request:
| Field | Description | Data Type | Required? |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING: Enumerated | TRUE |
| id | The customerID or any other agreed upon ID. | 64 STRING | TRUE |
| password | The password associated with the customerID. | 64 STRING | FALSE |
| productID | Item's ID | 64 STRING | TRUE |
| productIDtype | productID type (Distributor, Supplier) | 64 STRING | TRUE |
| filterColorArray | An array of different Colors the Inventory is requested | 256 STRING ARRAY | FALSE |
| filterSizeArray | An array of different Sizes the Inventory is requested | 256 STRING ARRAY | FALSE |
| filterSelectionArray | An array of different selections (generic) other than Color and Size the Inventory is requested | 256 STRING ARRAY | FALSE |
### Reply:
| Field | Description | Data Type | Required? |
| --------- | --- | --- | --- |
| productID | The associated product. | 64 STRING | TRUE |
| productVariationInventoryArray | An array of inventory levels grouped by variation. | ARRAY | FALSE |
| productCompanionInventoryArray | Array of companion items' inventory levels. | ARRAY | FALSE |
| errorMessage | Response for any error requiring notification to requestor | 256 STRING: Enumerated | FALSE |
| CustomMessageArray | An array of custom data that the supplier/distributor is free to implement in any way they see fit. | ARRAY | FALSE |
### ProductVariationInventory Object
| Field | Description | Data Type | Required? |
| --- | --- | --- | --- |
| partID | The associated part | 64 STRING | TRUE |
| partDescription | Part's description | 256 STRING | FALSE |
| partBrand | Part's brand | 64 STRING | FALSE |
| priceVariance | Variance from requested part's price | 64 STRING | FALSE |
| quantityAvailable | The quantity available | 64 STRING | TRUE |
| attributeColor | Description of the color of the part | 64 STRING | FALSE |
| attributeSize | Description of the size of the part | 64 STRING | FALSE |
| attributeSelection | Description of the generic selection criteria of the part | 64 STRING | FALSE |
| AttributeFlexArray | Array of the part's attributes | ARRAY | FALSE |
| customProductMessage | Message from the supplier regarding the stock | 256 STRING | FALSE |
| entryType | Record type (exact, alternate) | 64 STRING | FALSE |
| validTimestamp | Datetime inventory is available | ISO 8601 | FALSE |
### ProductCompanionInventory Object
| Field | Description | Data Type | Required? |
| ------ | --- | --- | --- |
| partID | The companion part | 64 STRING | TRUE |
| partDescription | Part's description | 256 STRING | FALSE |
| partBrand | Part's brand | 64 STRING | FALSE |
| price | Companion item price | 64 STRING | FALSE |
| quantityAvailable | The quantity available | 64 STRING | TRUE |
| attributeColor | Description of the color of the part | 64 STRING | FALSE |
| attributeSize | Description of the size of the part | 64 STRING | FALSE |
| attributeSelection | Description of the generic selection criteria of the part | 64 STRING | FALSE |
| entryType | Record type (exact, alternate) | 64 STRING | FALSE |
| AttributeFlexArray | Array of the part's attributes | ARRAY | FALSE |
| customProductMessage | Message from the supplier regarding the stock | 256 STRING | FALSE |
| validTimestamp | Datetime inventory is available | ISO 8601 | FALSE |
### CustomMessage Object
Suppliers and distributors are free to use the custom array however they wish.
### AttributeFlex Object
| Field | Description | Data Type | Required? |
| ----- | --------------- | ---------- | --------- |
| ID | Attribute ID | 64 STRING | FALSE |
| Name | Attribute Name | 64 STRING | FALSE |
| Value | Attribute Value | 256 STRING | FALSE |
The Supplier can use this ARRAY to define additional unique attributes about the product. An example could be for an item which has choices about thickness which can be made.
In this ID = "1"; Name = "Thickness"; Value = "0.10 inches"
## CustomProductMessage Object
Suppliers and distributors are free to use this custom message String however they wish.
Enumerated Type Restrictions
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING: Enumerated | 1.0.0 1.2.1 |
| --------- | -------------------------------------------------------- | --------------------- | ----------- |
| errorMessage | Response for any error requiring
notification to requestor | 256 STRING: Enumerated | 100: ID (customerID) not found
105: Authentication Credentials failed
110: Authentication Credentials required
115: wsVersion not found
120: The following field(s) are required [Comma Delimited field names]
125: Not Supported
200: ProductID not found
205: ProductColor not found
210: ProductSize not found
999: General Error – Contact the System Service Provider |
| ------------ | ---------------------------------------------------------------------------- | ---------------------- | ------------------------------ |
## Code Samples
To be published under separate specification: "Inventory Interface Supplier/Distributor Developer Guide"
[Inventory Interface Supplier Developer Guide 1.2.1](https://docs.google.com/document/d/1fiFJy9_7ZHCR7sh7nQIr5PykXynXJYIkpxuTwOOfqCU/edit?usp=sharing)
### DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of the "Interface Data Use Guidelines Standards"
## Validation of Services
Before you publish your endpoint, please ensure that it adheres to the [promostandards.org](https://promostandards.org) spec by using the web service validation tool.
[https://services.promostandards.org/webserviceValidator/home](https://services.promostandards.org/webserviceValidator/home)
Select the service, version, method and input your endpoint. If the endpoint is correct you should receive a message of:
"The XML response is valid."
© 2021 PromoStandards
## Diagrams
### getFilterValues
```mermaid
classDiagram
class getFilterValuesRequest {
+String wsVersion
+String id
+String password
+String productID
+String productIDtype
}
class getFilterValuesReply {
+String productID
+StringArray filterColorArray
+StringArray filterSizeArray
+StringArray filterSelectionArray
+String errorMessage
}
getFilterValuesRequest --> getFilterValuesReply : returns
```
### getInventoryLevels
```mermaid
classDiagram
class getInventoryLevelsRequest {
+String wsVersion
+String id
+String password
+String productID
+String productIDtype
+StringArray filterColorArray
+StringArray filterSizeArray
+StringArray filterSelectionArray
}
class getInventoryLevelsReply {
+String productID
+ProductVariationInventoryArray productVariationInventoryArray
+ProductCompanionInventoryArray productCompanionInventoryArray
+CustomMessageArray customMessageArray
+String errorMessage
}
class ProductVariationInventoryArray {
+ProductVariationInventory[] productVariationInventories
}
class ProductCompanionInventoryArray {
+ProductCompanionInventory[] productCompanionInventories
}
class CustomMessageArray {
+String[] customMessages
}
getInventoryLevelsRequest --> getInventoryLevelsReply : returns
```
---
## Inventory 2.0.0
Source: https://docs.psrestful.com/standards/inventory-2.0.0
# **PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES**

## **Inventory 2.0.0**
**Version**: 2.0.0
**Date**: 2017-17-03
---
## **Document Change Log**
| Version | Date | Reason for Change | Author |
| -------- | ---------- | ---------------------------------------------------------------------------------- | ------- |
| 2.0.0RC1 | 11/03/2017 | Initial Draft | Raj Mukherjee, Integration Team Lead, Hit Promotional Products Inc
Eric Shonebarger, CIO, Hit Promotional Products Inc |
| 2.0.0RC2 | 11/17/2017 | Renamed `GetFilterValuesReply` to `GetFilterValuesReponse`
Renamed `InventoryRequest` to `GetInventoryLevelsRequest`
Renamed `InventoryReply` to `GetInventoryLevelsResponse`
Changed id in `GetInventoryLevelsRequest` to be required
Changed password in `GetInventoryLevelsRequest` to be optional
All token types have been changed to strings. We used tokens very early on and then switched to strings.
Added `ErrorMessageArray` to match PO spec. Updated `ErrorMessage` object to include severity element.
Simplified filtering in both `GetInventoryLevelsRequest` and `GetyFilterValuesResponse` to use an object called "Filter".
`futureAvailableInventoryQuantity` and `futureAvailableInventoryTimeStamp` changed to required. The `FutureAvailabilityArray` is optional, so when specifying an element in the array, it must have this info.
Changed `address1`,`address2`,`address3` and `address4` to string array and renamed to `addressLines` to match the PO spec.
Updated default endpoint location to "[Endpoint URL]"
Renamed `partSize` to `labelSize` to match ProductData spec.
Renamed `colorName` to `partColor` to match ProductData spec.
Renamed `filterSize` to `labelSizeEnum` to match ProductData spec.
Renamed `futureAvailableInventoryTimeStamp` to `arrivalDate`
Changed `quantityAvailable` to use `Quantity` object
Changed `onOrderQuantity` to use `Quantity` object
Changed `futureAvailableInventoryQuantity` to use `Quantity` object
Changed `warehouseName` to be optional
Changed `Address` object to be optional | Paul Fleischman, Technical Lead, PCNA |
| 2.0.0RC2 | 11/20/2017 | Renamed `Warehouse` object to `InventoryLocation`
Renamed `warehouseName` to `inventoryLocationName`
Renamed `warehouseQuantity` to `inventoryLocationQuantity`
Renamed `warehouseId` to `inventoryLocationId` | Paul Fleischman, Technical Lead, PCNA |
| 2.0.0RC3 | 12/7/2017 | Renamed `ErrorMessage` to `ServiceMessage` | Paul Fleischman, Technical Lead, PCNA |
| 2.0.0RC4 | 02/26/2018 | Changes made based on February meeting:
Removed `partId` element from `GetFilterValuesRequest`
Created a `partIdArray` within the Filter object
Changed the `FilterArray` to not be an array within `GetFilterValuesResponse`. A single instance is all that is needed because of the new `partIdArray` within the Filter object. | Paul Fleischman, Technical Lead, PCNA |
| 2.0.0RC4 | 05/10/2018 | On the `SharedObjectsInventory.xsd`:
o added mentioned `partIdArray` definition
o Removed `FilterValues` definition
o Replaced the `partId` with a `partIdArray` on the Filter definition. | Raj Mukherjee, Integration Team Lead, Hit Promotional Products Inc
Tim Dietrich, NetSuite Integrator / PromoStandards Implementer |
| 2.0.0 | 07/24/2018 | On the `SharedObjectsInventory.xsd`:
added `replenishmentLeadTime`
Removed `SelectionArray` definition from Filter object | Raj Mukherjee, Integration Team Lead, Hit Promotional Products Inc |
---
## **Contributors**
- **Raj Mukherjee**, Integration Team Lead, Hit Promotional Products, Inc
- **Paul Fleischman**, Technical Lead, PCNA
- **Eric Shonebarger**, CIO, Hit Promotional Products, Inc
- **Jon Norris**, VP of Operations, Starline
---
## **Abstract and Recommended Audience**
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. It provides details on the technology required to build the interface and offers sample code. The reader is expected to be fluent in web-based technologies.
---
## **Background Information**
Specifications are built using the Simple Object Access Protocol (SOAP) over HTTPS for secure communication. More information on SOAP can be found at [w3.org](http://www.w3.org/TR/soap12-part1/).
---
## **Service Details**
- **Function: [getFilterValues()](#function-getfiltervalues)**:
Gets the different variations on a product, broken out by size, colour, selection.
- **Function: [getInventoryLevels()](#function-getinventorylevels)**:
Provides inventory levels for a product by product and inventory location. It also provides future stock details.
---
## **Function: getFilterValues()**
**Description**: Gets the different variations on a product, broken out by size, color, and selection.
### **Request: GetFilterValuesRequest**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{2.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| productId | The Product Id | STRING | VARCHAR(64) | TRUE |
### **Reply: GetFilterValuesResponse**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| [FilterValues](#filtervalues) | An object containing the variations of a product by size, color, selection, etc. | OBJECT | OBJECT | FALSE |
| [ServiceMessageArray](#servicemessage-object) | Error message object Array | OBJECT ARRAY | OBJECT ARRAY | FALSE |
### **FilterValues**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| productId | The product id | VARCHAR(64) | VARCHAR(64) | TRUE |
| [Filter](#filter) | A Filter Object that contains the variations of a product by part, size, and/or color. | OBJECT | OBJECT | TRUE |
---
## **Function: getInventoryLevels()**
**Description**: Gets the different inventory levels for the product broken out by product and inventory location. It also provides details on future stock for the product.
### **Request: getInventoryLevelsRequest**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{2.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customer Id or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the Id. | STRING | VARCHAR(64) | FALSE |
| productId | The Product Id | STRING | VARCHAR(64) | TRUE |
| [Filter](#filter) | Filter the response of `getInventoryLevels` by using a portion of the filter information received in the response of `GetFilterValuesRequest`. | OBJECT | OBJECT | FALSE |
### **Reply: getInventoryLevelsResponse**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| [Inventory](#inventory) | An object containing inventory by part and inventory location. Also shows future inventory by part and inventory location. | OBJECT | OBJECT | FALSE |
| [ServiceMessageArray](#servicemessage-object) | Service message object | OBJECT | OBJECT | FALSE |
### **Inventory**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-----------|-------------|----------------|---------------|-----------|
| productId | The product id | VARCHAR(64) | VARCHAR(64) | TRUE |
| [PartInventoryArray](#partinventory) | An array of ProductVariation objects | OBJECT ARRAY | OBJECT ARRAY | FALSE |
---
### PartInventoryArray
An array of ProductVariation objects
### PartInventory
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|--------------------|-----------|
| partId | The part id | VARCHAR(64) | VARCHAR(64) | TRUE |
| mainPart | A boolean value indicating if this is a main part of the product. In a tumbler with an optional lid configuration, the parts associated with the tumbler would be set to TRUE. The parts associated with the Lid would be set to false. | BOOLEAN | BOOLEAN | TRUE |
| partColor | Description of the color of the part | VARCHAR(64) | VARCHAR(64) | FALSE |
| labelSize | The apparel items tagged size. Enumerated values: \{6XS,5XS,4XS,3XS,2XS,XS,S,M,L,XL,2XL,3XL,4XL,5XL,6XL,CUSTOM\} CUSTOM is used for any size that does not match one of the other sizes. For example 7XL and 8XL would return CUSTOM. To identify the actual size when CUSTOM is returned, reference the Product Data endpoint. | ENUMERATED STRING | VARCHAR(6) | FALSE |
| partDescription | Description of the part | VARCHAR(256) | VARCHAR(256) | FALSE |
| quantityAvailable | A quantity object containing the sum of inventory of all inventory locations of the part | QUANTITY OBJECT | QUANTITY OBJECT | FALSE |
| manufacturedItem | Indicates that the supplier produces this part according to demand. The supplier may keep a limited amount of inventory or inventory may be 0. | BOOLEAN | BOOLEAN | TRUE |
| buyToOrder | Indicates that the supplier purchases this product to order. The supplier may keep a limited amount of inventory, show available inventory to buy, or it might be 0. | BOOLEAN | BOOLEAN | TRUE |
| replenishmentLeadTime | Time to replenish buy to order or made to order | INT(3) | INT(3) | FALSE |
| attributeSelection | A string describing the attribute of the product other than size and color | VARCHAR(64) | VARCHAR(64) | FALSE |
| [InventoryLocationArray](#inventorylocation) | An array of InventoryLocation objects | ARRAY | ARRAY | FALSE |
| lastModified | A date timestamp in UTC specifying the last time inventory was modified. | DATETIME | DATETIME | FALSE |
## InventoryLocation
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------------|-------------------------------------------------------------------------------------------------|-----------------|-----------------|-----------|
| inventoryLocationId | The inventory location id | VARCHAR(64) | VARCHAR(64) | TRUE |
| inventoryLocationName | The name of the inventory location | VARCHAR(64) | VARCHAR(64) | FALSE |
| postalCode | The postal code | STRING | VARCHAR(10) | FALSE |
| country | The country in ISO 3166-2 format | STRING | VARCHAR(2) | FALSE |
| [inventoryLocationQuantity](#quantity) | An object which contains available to order quantity for this inventory location | QUANTITY OBJECT | QUANTITY OBJECT | TRUE |
| [FutureAvailabilityArray](#futureavailability-object) | Array of FutureAvailability objects | OBJECT ARRAY | OBJECT ARRAY | FALSE |
## **FutureAvailabilityArray**
This array provides details about the future availability of specific parts of a product.
### FutureAvailability
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| [Quantity](#quantity) | Future net available inventory quantity for the part/inventory location combination indicating goods on order or future quantity at manufactured goods. | QUANTITY OBJECT | QUANTITY OBJECT | TRUE |
| availableOn | A date timestamp in UTC specifying the valid timestamp depicting when the inventory will be available for allocation to sales orders. | DATETIME | DATETIME | TRUE |
## **Filter**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| partIdArray | A list of part ids to be used when filtering | STRING ARRAY | VARCHAR(64) | FALSE |
| LabelSizeArray | An array of labelSize object | STRING ARRAY | VARCHAR(64) | FALSE |
| PartColorArray | A list of colors to be used when filtering | STRING ARRAY | VARCHAR(64) | FALSE |
## **Quantity**
This object provides details about the quantity value and its unit of measure.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| value | The quantity value. | DECIMAL | DECIMAL(12,4) | TRUE |
| uom | The unit of measure; values are enumerated.
Values include:
BX (Box),
CA (Case),
DZ (Dozen),
EA (Each),
KT (Kit),
PR (Pair),
PK (Package),
RL (Roll),
ST (Set),
SL (Sleeve),
TH (Thousand) | ENUMERATED STRING | VARCHAR(2) | TRUE |
## **Appendix A: Error Messages**
### **ServiceMessage Object**
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|-------------|----------------|---------------|-----------|
| code | The numerical value of the code | INT | INT | TRUE |
| description | Response for any message requiring notification to requestor | STRING | VARCHAR(256) | TRUE |
| severity | The severity of the message. Values are enumerated: \{`Error`, `Information`, `Warning`\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
## **Standardized Codes**
Codes range from `100-199` for standardized error codes.
The number `999` is reserved for a "General Error - Contact System Service Provider".
| Code | Description |
| ----- | ------------------------------------------------------------------------ |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 999 | General Error – Contact the System Service Provider Details: [Details] |
**Service Specific Code**: These error codes are only for this service.
| Code | Description |
| ----- | ------------------------------------------------------------------------ |
| 600 | Product Id not found |
| 610 | Data violation: \{0\}
(Occurs when a supplier does a pre-check on the data and finds that there is a mismatch of data and/or there are incorrect calculations)|
| 620 | Field is not supported: \{0\} |
| 630 | Part Id not found |
---
## Diagrams
### getFilterValues
```mermaid
classDiagram
class GetFilterValuesRequest {
+String wsVersion
+String id
+String password (optional)
+String productId
}
class GetFilterValuesResponse {
+FilterValues filterValues
+ServiceMessageArray serviceMessageArray
}
class FilterValues {
+String productId
+Filter filter
}
class Filter {
+String[] partIdArray
+String[] labelSizeArray
+String[] partColorArray
}
GetFilterValuesRequest --|> GetFilterValuesResponse : requests
GetFilterValuesResponse "1" -- "1" FilterValues : contains
FilterValues "1" -- "1" Filter : contains
```
### getInventoryLevels
```mermaid
classDiagram
class GetInventoryLevelsRequest {
+String wsVersion
+String id
+String password (optional)
+String productId
+Filter filter
}
class GetInventoryLevelsResponse {
+Inventory inventory
+ServiceMessage serviceMessage
}
class Inventory {
+String productId
+PartInventoryArray partInventoryArray
+InventoryLocationArray inventoryLocationArray
+DateTime lastModified
}
class PartInventory {
+String partId
+Boolean mainPart
+String partColor
+String labelSize
+String partDescription
+QuantityObject quantityAvailable
+Boolean manufacturedItem
+Boolean buyToOrder
+Integer replenishmentLeadTime
+String attributeSelection
}
class InventoryLocation {
+String inventoryLocationId
+String inventoryLocationName
+String postalCode
+String country
+QuantityObject inventoryLocationQuantity
+FutureAvailabilityArray futureAvailabilityArray
}
GetInventoryLevelsRequest --|> GetInventoryLevelsResponse : requests
GetInventoryLevelsResponse "1" -- "1" Inventory : contains
Inventory "1" -- "*" PartInventory : contains
Inventory "1" -- "*" InventoryLocation : contains
```
### Class Diagram for ServiceMessage and QuantityObject
```mermaid
classDiagram
class ServiceMessage {
+Int code
+String description
+String severity
}
class QuantityObject {
+Decimal value
+String uom (unit of measure)
}
```
---
## Invoice 1.0.0
Source: https://docs.psrestful.com/standards/invoice-1.0.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

## Invoice 1.0.0
**Version**: 1.0.0
**Release Date**: 2020-01-27
## Document Change Log
| Version | Date | Reason for Change | Authors |
|---------|------------|-------------------|---------|
| 1.0.0 | 2020-01-27 | Version 1.0.0 Release | Stephen Luisser - Essent Corporation Peter Tu - AnyPromo Paul Fleischman - PCNA |
## Contributors
The following have contributed to the creation of this specification:
*Authors:*
- **Stephen Luisser** - Essent Corporation
- **Peter Tu** - AnyPromo
- **Paul Fleischman** - PCNA
## Abstract and Recommended Audience
This document describes the technologies for the integration of suppliers and distributors in the Promotional Products Industry. It provides sample code for the interface and assumes the reader is fluent in web-based technologies.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the
foundation for the web services protocol stack in order to provide a standards based secure form of
communication.
More information on SOAP can be found at [w3](http://www.w3.org/TR/soap12-part1/).
## **Service Details:**
- [getInvoices](#getinvoices)
- [getVoidedInvoices](#getvoidedinvoices)
## **`getInvoices()`**
This function sends invoices based on the criteria provided in the `queryType` field.
### **Request: GetInvoiceRequest**
| Field | Description | Data Type | Required |
|-------------------|-----------------------------------------------------------|----------------|----------|
| wsVersion | Standard Version of the Web Service (e.g., \{1.0.0\}) | 64 STRING | TRUE |
| id | customerId or other agreed upon Id | 64 STRING | TRUE |
| password | Password associated with customerId | 64 STRING | FALSE |
| queryType | Type of invoice query requested | Enumerated INT | TRUE |
| referenceNumber | PO number for queryType =1 or invoice number for queryType = 2 | 64 STRING | FALSE |
| requestedDate | Date of the invoice for queryType = 3 | DATE | FALSE |
| availableTimeStamp| Beginning date time since an invoice was made available in UTC | DATETIME | FALSE |
### **GetInvoiceRequest queryType**
| queryType | Short Name | Description |
|-----------|----------------------------|-------------|
| 1 | PO Number Search | Search for invoices by purchase order number |
| 2 | Invoice Number Search | Search for invoice by invoice number |
| 3 | Date Search | Search for invoices with an invoice date specified by the requestedDate |
| 4 | Available Date Time Search | Search for invoices that were made available by a date time greater than the value specified by availableTimeStamp |
### **Reply: GetInvoiceResponse**
| Field | Description | Data Type | Required |
|--------------------|------------------------------------|-----------|----------|
| [InvoiceArray](#invoice-object) | Array of invoice objects | ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object)| Array of ServiceMessage objects | OBJECT | FALSE |
## **Invoice Object**
| Field | Description | Data Type | Required |
|-------------------------|-----------------------------------------------------------------------------|-------------|----------|
| invoiceNumber | The invoice number | 64 STRING | TRUE |
| invoiceType | The type of invoice; values are enumerated: `INVOICE`, `CREDIT MEMO` | 64 STRING | TRUE |
| invoiceDate | The date the invoice was generated | DATE | TRUE |
| purchaseOrderNumber | The purchase order number | 64 STRING | FALSE |
| purchaseOrderVersion | The version of the purchase order number | 64 STRING | FALSE |
| [BillTo](#accountinfo-object) | The Bill To Address (AccountInfo Object) | OBJECT | FALSE |
| [SoldTo](#accountinfo-object) | The Sold To Address (AccountInfo Object) | OBJECT | FALSE |
| invoiceComments | General comments for the invoice | STRING | FALSE |
| paymentTerms | The terms of the invoice | 64 STRING | FALSE |
| paymentDueDate | The Date the invoice must be paid in full without incurring late charges | DATE | TRUE |
| currency | The currency of the invoice in ISO4217 format | STRING | TRUE |
| fobId | The fob point of the invoice | 64 STRING | FALSE |
| salesAmount | The amount of the sale in the specified currency | DOUBLE | TRUE |
| shippingAmount | The amount of the shipping charges in the specified currency | DOUBLE | TRUE |
| handlingAmount | The amount of the handling charges in the specified currency | DOUBLE | TRUE |
| taxAmount | The total amount of taxes in the currency specified | DOUBLE | TRUE |
| invoiceAmount | The total amount of the invoice in the currency specified. Note: invoiceAmount = salesAmount + shippingAmount + handlingAmount + taxAmount | DOUBLE | TRUE |
| advancePaymentAmount | The amount of any advanced payments in the currency specified. If the source system does not support including prepayments on an invoice this value should be set to zero. | DOUBLE | TRUE |
| invoiceAmountDue | The total of the invoice amount due after applying any prepayments in the currency specified. Note: invoiceAmountDue = invoiceAmount - advancePaymentAmount | DOUBLE | TRUE |
| invoiceDocumentUrl | The url to be able to download the physical invoice document. | 1024 STRING | FALSE |
| [InvoiceLineItemsArray](#invoicelineitem-object) | An array of invoice line item objects | OBJECT ARRAY | TRUE |
| [SalesOrderNumbersArray](#salesordernumber-object) | An array of sales order numbers included in the invoice | 64 STRING ARRAY | FALSE |
| [TaxArray](#tax-object) | An array of tax objects. The sum of the taxes within this array should equal the value in the taxAmount field. | OBJECT ARRAY | FALSE |
| invoicePaymentUrl | The url used to submit payment for the invoice | 1024 STRING | FALSE |
## **InvoiceLineItem Object**
| Field | Description | Data Type | Required |
|------------------------------|---------------------------------------------------------------|-------------|----------|
| invoiceLineItemNumber | The line item number of the line item | DOUBLE | FALSE |
| productId | The productId when the line item applies to a product | 64 STRING | FALSE |
| partId | The partId when the line item applies to a product | 64 STRING | FALSE |
| chargeId | The chargeId when the line item applies to a charge | 64 STRING | TRUE |
| purchaseOrderLineItemNumber | The line item number of the purchase order that the invoice references | 64 STRING | FALSE |
| orderedQuantity | The quantity ordered by the referenced purchase order | DOUBLE | FALSE |
| invoiceQuantity | The quantity of the line item invoiced | DOUBLE | TRUE |
| backOrderedQuantity | The quantity of the line item backordered | DOUBLE | FALSE |
| quantityUOM | The unit of measure of the `orderQuantity`, `invoicedQuantity`, and ` ` | 2 STRING (FACIT) | TRUE |
| lineItemDescription | A textual description of the line item | 1024 STRING| TRUE |
| unitPrice | The price of the unit in the currency of the purchase order | DOUBLE | TRUE |
| discountAmount | An amount of discount applied to the item | DOUBLE | FALSE |
| extendedPrice | The extended price for the line item. Note: extendedPrice = (unitPrice * invoicedQuantity) - discountAmount | DOUBLE | TRUE |
| distributorProductId | The distributor’s productId for the item when the line item applies to a product | 64 STRING | FALSE |
| distributorPartId | The distributor’s partId for the item when the line item applies to a product | 64 STRING | FALSE |
## **AccountInfo Object**
| Field | Description | Data Type | Required |
|-----------------|---------------------------------------------------------------|-------------|----------|
| accountName | The name of the account that will be invoiced for the purchase order. This also represents the `companyName` field from the PO. | 64 STRING | FALSE |
| accountNumber | The account number invoiced. | 64 STRING | FALSE |
| attentionTo | Attention To (first and last name of contact) | 64 STRING | FALSE |
| address1 | Address line 1 | 35 STRING | FALSE |
| address2 | Address line 2 | 35 STRING | FALSE |
| address3 | Address line 3 | 35 STRING | FALSE |
| city | The city | 30 STRING | FALSE |
| region | The 2 character US state abbreviation or 2-3 character non-US region. | 3 STRING | FALSE |
| postalCode | The postal code | 10 STRING | FALSE |
| country | The country in ISO 3166-2 format | 2 STRING | FALSE |
| email | The email | 128 STRING | FALSE |
| phone | The phone number | 32 STRING | FALSE |
## **Tax Object**
| Field | Description | Data Type | Required |
|------------------|-----------------------------------------------------------------------------|-------------|----------|
| taxType | The type of tax the identifier applies to. Values are enumerated: “SALES”, “HST/GST”, “PST”, “VAT”. | 64 STRING (FACIT) | TRUE |
| taxJurisdiction | The jurisdiction for the tax. For example, NJ, PA or Philadelphia City. | 64 STRING | TRUE |
| taxAmount | The amount of tax | DOUBLE | TRUE |
## **SalesOrderNumber Object**
| Field | Description | Data Type | Required |
|------------------|-------------|-----------|----------|
| salesOrderNumber | The sales order associated with the invoice. | 64 STRING | TRUE |
## **`getVoidedInvoices()`**
This function will send voided invoices based on the criteria provided in the `queryType` field.
### **Request: GetVoidedInvoiceRequest**
| Field | Description | Data Type | Required |
|--------------------|-----------------------------------------------------------------------------|----------------|----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customerId or any other agreed upon Id | 64 STRING | TRUE |
| password | The password associated with the customerId | 64 STRING | FALSE |
| queryType | The type of invoice query requested | Enumerated INT | TRUE |
| referenceNumber | The purchase order number for queryType =1 or the invoice number for queryType = 2 | 64 STRING | FALSE |
| requestedDate | The date the invoice was voided for queryType = 3. This field does not include a time component | DATE | FALSE |
| availableTimeStamp | Beginning date time since a voided invoice was made available in UTC | DATETIME | FALSE |
### **Reply: GetVoidedInvoiceResponse**
| Field | Description | Data Type | Required |
|---------------------|--------------------------------------------------|-----------|----------|
| [VoidedInvoiceArray](#voidedinvoice-object) | An array of voided invoice objects | ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects | OBJECT | FALSE |
### **VoidedInvoice Object**
| Field | Description | Data Type | Required |
|---------------------|-----------------------------------------------------------------------------|-----------|----------|
| invoiceNumber | The invoice number | 64 STRING | TRUE |
| voidDate | The date the invoice was voided. This field does not include a time component and it is up to the consuming party to determine if the data should be adjusted if the publishing party is on the other side of the international date line. | DATE | TRUE |
## **Appendix A: Service Messages**
### **ServiceMessage Object**
| Field | Description | Data Type | Required |
|-------------|--------------------------------------------|-----------|----------|
| code | The numerical value of the code | INT | TRUE |
| description | Response for any message requiring notification to requestor | 256 STRING | TRUE |
| severity | The severity of the message. Values are enumerated: \{Error, Information, Warning\} | ENUMERATED 64 STRING | TRUE |
### **Standardized Codes**
The range of `100-199` has been reserved for standardized error codes. The number `999` has been reserved for an error code that is a “General Error - Contact System Service Provider”.
| Code | Description |
|------|-------------|
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 999 | General Error – Contact the System Service Provider |
### **Service Specific Codes**
These error codes are only for this service.
| Code | Description |
|------|-------------|
| 901 | queryType not found |
| 902 | queryType not supported |
| 903 | No Invoices were found for the requested criteria |
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of the “Interface Data Use Guidelines Standards”.
## VALIDATION OF SERVICES
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by using the web
service validation tool located at: https://services.promostandards.org/webserviceValidator/home.
Select the service, version, method and input your endpoint. If the endpoint is correct, you should receive a message of:
"The XML response is valid."
## Diagrams
### getInvoices Function
```mermaid
classDiagram
class GetInvoiceRequest {
+String wsVersion
+String id
+String password
+Int queryType
+String referenceNumber
+Date requestedDate
+DateTime availableTimeStamp
}
class GetInvoiceResponse {
+InvoiceArray invoiceArray
+ServiceMessageArray serviceMessageArray
}
class Invoice {
+String invoiceNumber
+String invoiceType
+Date invoiceDate
+String purchaseOrderNumber
+String purchaseOrderVersion
+AccountInfo billTo
+AccountInfo soldTo
+String invoiceComments
+String paymentTerms
+Date paymentDueDate
+String currency
+String fobId
+Double salesAmount
+Double shippingAmount
+Double handlingAmount
+Double taxAmount
+Double invoiceAmount
+Double advancePaymentAmount
+Double invoiceAmountDue
+String invoiceDocumentUrl
+InvoiceLineItemsArray invoiceLineItemsArray
+SalesOrderNumbersArray salesOrderNumbersArray
+TaxArray taxArray
+String invoicePaymentUrl
}
class AccountInfo {
+String accountName
+String accountNumber
+String attentionTo
+String address1
+String address2
+String address3
+String city
+String region
+String postalCode
+String country
+String email
+String phone
}
class InvoiceLineItem {
+Double invoiceLineItemNumber
+String productId
+String partId
+String chargeId
+String purchaseOrderLineItemNumber
+Double orderedQuantity
+Double invoiceQuantity
+Double backOrderedQuantity
+String quantityUOM
+String lineItemDescription
+Double unitPrice
+Double discountAmount
+Double extendedPrice
+String distributorProductId
+String distributorPartId
}
class SalesOrderNumber {
+String salesOrderNumber
}
class Tax {
+String taxType
+String taxJurisdiction
+Double taxAmount
}
class ServiceMessage {
+Int code
+String description
+String severity
}
GetInvoiceRequest --> GetInvoiceResponse : returns
GetInvoiceResponse --> Invoice : contains
Invoice --> AccountInfo : contains
Invoice --> InvoiceLineItem : contains
Invoice --> SalesOrderNumber : contains
Invoice --> Tax : contains
GetInvoiceResponse --> ServiceMessage : contains
```
### getVoidedInvoices Function
```mermaid
classDiagram
class GetVoidedInvoiceRequest {
+String wsVersion
+String id
+String password
+Int queryType
+String referenceNumber
+Date requestedDate
+DateTime availableTimeStamp
}
class GetVoidedInvoiceResponse {
+VoidedInvoiceArray voidedInvoiceArray
+ServiceMessageArray serviceMessageArray
}
class VoidedInvoice {
+String invoiceNumber
+Date voidDate
}
GetVoidedInvoiceRequest --> GetVoidedInvoiceResponse : returns
GetVoidedInvoiceResponse --> VoidedInvoice : contains
GetVoidedInvoiceResponse --> ServiceMessage : contains
```
---
## Media Content 1.1.0
Source: https://docs.psrestful.com/standards/media-content-1.1.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

## Media Content Service
**Version**: 1.1.0
**Release Date**: 2017-07-18
## Document Change Log
| Version | Date | Reason for Change | Authors |
|---------|------------|-------------------|---------|
| 1.1.0 | 22017-07-18 | Resolves the reported validation issue where Location and Decoration array require at least one element when these arrays should be optional. | Paul Fleischman - PCNA |
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface. Additionally, this document will provide sample code in order to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at [w3](http://www.w3.org/TR/soap12-part1/)
---
## **Media Content**
### **Summary:**
Provides a mechanism to get a media content by specific parameters like (last change date, product id, part id, media type, etc). This allows the consumer of the service to obtain specific media content for their needs.
## Service Details:
- [getMediaContent](#function-getmediacontent)
- [getMediaDateModified](#getmediadatemodified)
## `getMediaContent()`
### **Request: GetMediaContentRequest**
| Field | Description | Data Type | Required? |
|-------------|--------------------------------------------------------------------------------------------------|-------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | YES |
| id | The customer Id or any other agreed upon Id. | 64 STRING | YES |
| password | The password associated with the Id | 64 STRING | NO |
| cultureName | The language culture name. Null assumes en-US. | ISO 639x | NO |
| [mediaType](#media-content-filtering) | The type of media to return | STRING FACET| YES* |
| productId | The product Id | 64 STRING | YES |
| partId | The part Id associated to the product Id | 64 STRING | NO |
| classType | Classification of the media | INT | NO |
### **Reply: GetMediaContentDetailsResponse**
| Field | Description | Data Type | Required? |
|--------------------|--------------------------------------------------------------------------------------------------|---------------|-----------|
| [MediaContentArray](#mediacontent-object) | An array of media content objects | OBJECT ARRAY | NO |
| [errorMessage](#errormessage-object) | Response for any error requiring notification to requestor | OBJECT | NO |
### **MediaContent Object**
| Field | Description | Data Type | Required? |
|-----------------|--------------------------------------------------------------------------------------------------|---------------|-----------|
| productId | The associated Product Id | 64 STRING | YES |
| partId | The part Id associated to the product Id | 64 STRING | NO |
| url | The URL of the media location | 1024 STRING | YES |
| mediaType | The type of media | STRING FACET | YES |
| [ClassTypeArray](#classtype-object) | An array of ClassType objects that classify of the media | OBJECT ARRAY | YES |
| fileSize | The file size | DOUBLE | NO |
| width | Width | INT | NO |
| height | Height | INT | NO |
| dpi | Dots per inch | INT | NO |
| color | The color description | 256 STRING | NO |
| [DecorationArray](#decoration-object) | An array of decoration objects | OBJECT ARRAY | NO |
| [LocationArray](#location-object) | An array of location objects | OBJECT ARRAY | NO |
| description | Information about the media | 1024 STRING | NO |
| singlePart | Identifies whether the partId one to one corresponds with the image | BOOLEAN | YES |
| changeTimeStamp | The date time since last change in UTC | ISO 8601 | NO |
### **ClassType Object**
| Field | Description | Data Type | Required? |
|--------------- |--------------------------------------------------------------------------------------------------|-----------|-----------|
| classTypeId | The classification of the media | INT | YES |
| classTypeName | The classification short name | 64 STRING | YES |
### **Decoration Object**
| Field | Description | Data Type | Required? |
|----------------|--------------------------------------------------------------------------------------------------|-----------|-----------|
| decorationId | The decoration id associated with the media | INT | YES |
| decorationName | The name of the decoration associated with the id | 64 STRING | YES |
### **Location Object**
| Field | Description | Data Type | Required? |
|--------------|--------------------------------------------------------------------------------------------------|-----------|-----------|
| locationId | The location id associated with the media | INT | YES |
| locationName | The name of the location associated with the id | 64 STRING | YES |
## **Media Date Modified**
### **Summary:**
Provides a mechanism to get all product and/or part ids, of media, that changed since a specified date.
## getMediaDateModified()
### **Request: GetMediaDateModifiedRequest**
| Field | Description | Data Type | Required? |
|------------------|---------------------------------------------------------------------------------------------------|-------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | YES |
| id | The customer Id or any other agreed upon Id | 64 STRING | YES |
| password | The password associated with the Id | 64 STRING | NO |
| cultureName | The language culture name. Null assumes en-US | ISO 639x | NO |
| changeTimeStamp | Beginning date time since last change in UTC | ISO 8601 | YES |
### **Reply: GetMediaDateModifiedResponse**
| Field | Description | Data Type | Required? |
|------------------------|--------------------------------------------------------------------------------------------------|---------------|-----------|
| [MediaDateModifiedArray](#mediadatemodified-object) | An array of MediaDateModified objects | OBJECT ARRAY | NO |
| [errorMessage](#errormessage-object) | Response for any error requiring notification to requestor | OBJECT | NO |
### **MediaDateModified Object**
| Field | Description | Data Type | Required? |
|-----------|--------------------------------------------------------------------------------------------------|-----------|-----------|
| productId | The associated Product Id | 64 STRING | YES |
| partId | The part Id associated to the product Id | 64 STRING | NO |
## Enumerated Type Restrictions
| Enumerated Type | Description | Values |
|-----------------|-----------------------------------|--------|
| mediaType | The basic media type | Image - Returns images, Video - Returns videos, Audio - Returns audios, Document - Returns documents |
### Valid values for class type and class name:
| classType | Class Name | Description |
|-----------|---------------|------------------------------------------------------------------------------------------------------|
| 0-499 | Reserved | Reserved for future use |
| 500-999 | Custom | Custom class types for implementation specific use. These identifiers |
| 1000 | Unspecified | Unknown or unspecified shot. This value means the shot type is unavailable for the media type. |
| 1001 | Blank | The shot is of blank media |
| 1002 | Decorated | The shot is of decorated media |
| 1003 | Alternate | The shot is alternate. This may indicate the product is combined with other media to stage a scene. |
| 1004 | Swatch | The shot is of a swatch |
| 1005 | Custom | The shot is custom which does not fall into any specific type |
| 1006 | Primary | The primary image |
| 1007 | Front | Front view |
| 1008 | Rear | Rear view |
| 1009 | Right | Right view |
| 1010 | Left | Left view |
| 1011 | Top | Top view |
| 1012 | Bottom | Bottom view |
| 1013 | Inside | Inside view |
| 1014 | Outside | Outside view |
| 2000 | Standard | Standard Definition |
| 2001 | High | High definition |
| 3000 | Podcast | Podcast |
| 4000 | Specs | Specification sheets |
| 4001 | ProductSafety| Product safety information |
| 4002 | Facts | Fact sheets |
| 4003 | Compliance | Compliance documents |
| 4004 | ArtTemplate | Art templates |
| 4005 | Marketing | Marketing material |
## Appendix A: Error Messages
### **ErrorMessage Object**
| Field | Description | Data Type | Required? |
|-------------|--------------------------------------------------------------------------------------------------|------------|-----------|
| code | The numerical value of the code | NUMBER | YES |
| description | Response for any error requiring notification to requestor | 256 STRING | YES |
**Standardized Codes:** The range of `100-199` has been reserved for standardized error codes. The number `999` has been reserved for an error codes that is a “General Error - Contact System Service Provider”
### **Error Codes and Descriptions**
| Code | Description |
|------|-------------------------------------------------------------------------------------------------|
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 130 | Product Id not found |
| 135 | Product color not found |
| 140 | Part Id not found |
| 145 | Part color not found |
| 150 | Part size not found |
| 155 | Invalid Date Format |
| 160 | No Results Found |
| 999 | General Error – Contact the System Service Provider |
## Media Content Filtering
In [GetMediaContentRequest](#request-getmediacontentrequest) the user allows to filter by `mediaType`. The documentation & the WSDL file tells you that it is required.
However, several Suppliers allows to send empty value returning all types of media in one call.
We value that so we don't required the `mediaType` however we do default to `Image` if the user doesn't provide `media_type` and the supplier requires it.
Here is the list of supplier's affected by this decision:
- Ariel
- bambams
- Cutter
- debco
- ETS
- GEM
- Gold
- goldstar
- Hand Stands
- HIT
- Hirsch
- Hub Pen
- BIC
- LMRK
- PCNA
- PLAYINGCARDSONDEMAND
- ProTowels
- Scrub Authority
- SNUGZ
- Spector
- STAR
- stormcreek
- StRegis
- sunjoy
- terry
- vantage
## Validation of Services
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by using the web service validation tool.
https://services.promostandards.org/webserviceValidator/home
Select the service, version, method and input your endpoint. If the endpoint is correct you should receive a message of:
"The XML response is valid."
## Code Samples
To be published under separate specification: “Media Content Standards Use Examples”
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of the “Interface Data Use Guidelines Standards”
---
## Diagrams
### getMediaContent Function
```mermaid
classDiagram
class GetMediaContentRequest {
-wsVersion: String
-id: String
-password: String?
-cultureName: String?
-mediaType: String
-productId: String
-partId: String?
-classType: Int?
}
class GetMediaContentResponse {
-MediaContentArray: MediaContent[]
-errorMessage: ErrorMessage?
}
class MediaContent {
-productId: String
-partId: String?
-url: String
-mediaType: String
-ClassTypeArray: ClassType[]
-fileSize: Double?
-width: Int?
-height: Int?
-dpi: Int?
-color: String?
-DecorationArray: Decoration[]
-LocationArray: Location[]
-description: String?
-singlePart: Boolean
-changeTimeStamp: String?
}
class ClassType {
-classTypeId: Int
-classTypeName: String
}
class Decoration {
-decorationId: Int
-decorationName: String
}
class Location {
-locationId: Int
-locationName: String
}
class ErrorMessage {
-code: Number
-description: String
}
GetMediaContentRequest --> GetMediaContentResponse
GetMediaContentResponse --> MediaContent
MediaContent --> ClassType
MediaContent --> Decoration
MediaContent --> Location
```
### getMediaDateModified Function
```mermaid
classDiagram
class GetMediaDateModifiedRequest {
-wsVersion: String
-id: String
-password: String?
-cultureName: String?
-changeTimeStamp: String?
}
class GetMediaDateModifiedResponse {
-MediaDateModifiedArray: MediaDateModified[]
-errorMessage: ErrorMessage?
}
class MediaDateModified {
-productId: String
-partId: String?
}
class ErrorMessage {
-code: Number
-description: String
}
GetMediaDateModifiedRequest --> GetMediaDateModifiedResponse
GetMediaDateModifiedResponse --> MediaDateModified
```
---
## Order Shipment Notification 1.0.0
Source: https://docs.psrestful.com/standards/order-shipment-notification-1.0.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

### **Order Shipment Notification (OSN)**
- **Version:** 1.0.0
- **Date:** 2016-01-29
## **Document Change Log**
| Version | Date | Reason for Change | Author |
|---------|------------|----------------------------------------------------|---------------------------------------------------------|
| 0.0.1 | 2015-10-30 | Initial Draft | Stephen Luisser, VP of Research, Essent Corporation |
| 0.0.2 | 2015-11-24 | Changed shipmentDestinationType to optional. | Stephen Luisser, VP of Research, Essent Corporation |
| 0.0.3 | 2015-12-17 | 1. Changed "ShipmentLocations" to "ShipmentLocation". 2. Changed ErrorMessage to an object and added object definition. 3. Marked SalesOrderNumberArray as optional for when writing an ErrorMessage object. 4. Changed version to a subversion until published. 5. Changed “PurchaseOrderNumber “ to “purchaseOrderNumber”. 6. Made all non-customizable enums strings. 7. Created an Address object and referenced for ShipFrom and ShipTo. | Paul Fleischman, Technical Lead, PCNA |
| 0.0.4 | 2016-01-05 | 1. Consistency corrections2. Added Standardized Codes for ErrorMessage object3. Added supplierProductId, supplierPartId, distributorProductId, distributorPartId to Item array | Paul Fleischman, Technical Lead, PCNA |
| 0.0.5 | 2016-01-28 | 1. Removed 4th enumeration from Request.xsd file for query type 2. Added numerical values to documentation for query type parameters | Jon Norris, VP of Operations, Starline |
| 0.0.6 | 2016-01-28 | 1. Capitalized the Reply name “GetOrderShipmentNotification Response” 2. Created “OrderShipmentNotificationArray”: This is necessary to handle the ShipDateSearch queryType, as more than one PO will be returned at once. 3. Moved error message out into “GetOrderShipmentNotification Response” | Jon Norris, VP of Operations, Starline |
| 0.0.7 | 2016-01-29 | Renamed “SalesOrderNumberArray” to “SalesOrderArray” | Paul Fleischman, Technical Lead PCNA |
| 0.0.8 | 2016-03-01 | Added error codes range 300-399 that are specific to this service. | Jon Norris, VP of Operations, Starline |
| 1.0.0 | 2016-03-02 | Service moved into Production | Jon Norris, VP of Operations, Starline |
## **Abstract and Recommended Audience**
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. It discusses the technology required to build the interface and provides sample code for using the interface. The reader should be fluent in web-based technologies and familiar with the language they plan to consume the web service in.
## **Background Information**
All specifications use the Simple Object Access Protocol (SOAP) over HTTPS for a standards-based secure form of communication. More information on SOAP can be found at [w3.org](http://www.w3.org/TR/soap12-part1/).
## **Order Shipment Notification**
- **Summary:** Summary: Provides a mechanism to get shipment details by specific parameters like (purchase order
number, sales order number, or shipment date). This allows the consumer of the service to obtain
shipment information grouped by purchase order number and sales order number for their needs.
### **Function:**
`getOrderShipmentNotification()`
### **REQUEST: GetOrderShipmentNotificationRequest**
| Field | Description | Data Type | Required? |
|------------------------|---------------------------------------------------------------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. | 64 STRING: Enumerated | TRUE |
| id | The customerID or any other agreed upon ID. | 64 STRING | TRUE |
| password | The password associated with the customerID or agreed upon ID. | 64 STRING | FALSE |
| queryType | The type of query you wish to perform. | String Enumerated | TRUE |
| referenceNumber | The purchase order or sales order number. Required when the queryType is 1 or 2. | 64 STRING | FALSE |
| shipmentDateTimeStamp | The earliest date for shipments to return in UTC3. Required when the queryType is 3. ISO 8601 | DATE | FALSE |
**Notes:**
1. This indicates the type of query you are trying to perform. Valid Options:
- **queryType 1 (POSearch):** Query based on customer provided purchase order number.
- **queryType 2 (SOSearch):** Query based on vendor assigned sales order number.
- **queryType 3 (ShipDateSearch):** Query based on all shipments with a shipment date greater than the value specified in shipmentDateTimeStamp.
2. When the queryType is (PO Search), this value represents the customer provided purchase order number. When the queryType is (SO Search), this value represents the vendor assigned sales order number. When the queryType is (Shipment Date Search), this value is not used and should not be passed.
3. This field will indicate the date time for any shipment GREATER than the shipment date time stamp provided. Note: Make sure to account for some jitter between the two systems and make sure to specify your time request in UTC and not in the local time zone.
## **REPLY: GetOrderShipmentNotificationResponse**
| Field | Description | Data Type | Required? |
|--------------------------------|------------------------------------------------------------|-----------|-----------|
| [OrderShipmentNotificationArray](#ordershipmentnotificationarray-details) | An array of Order Shipment Notifications. | ARRAY | FALSE |
| [ErrorMessage](#errormessage--standardized-codes) | Response for any error requiring notification to requestor | OBJECT | FALSE |
#### **OrderShipmentNotificationArray Details:**
| Field | Description | Data Type | Required? |
|-----------------------|--------------------------------------------------------------|-----------|-----------|
| purchaseOrderNumber | The associated purchase order. | 64 STRING | TRUE |
| complete | All shipments for this purchase order are complete. | BOOLEAN | TRUE |
| [SalesOrderArray](#salesorder-details) | An array of sales orders associated with the purchase order. | ARRAY | FALSE |
#### **SalesOrder Details:**
| Field | Description | Data Type | Required? |
|-----------------------|---------------------------------------------------|-----------|-----------|
| salesOrderNumber | The associated sales order. | 64 STRING | TRUE |
| complete | All shipments for this sales order are complete. | BOOLEAN | TRUE |
| [ShipmentLocationArray](#shipmentlocation-details) | An array of shipment locations. | ARRAY | TRUE |
#### **ShipmentLocation Details:**
| Field | Description | Data Type | Required? |
|-------------------------|---------------------------------------------------|-----------|-----------|
| id | The id of the location. | NUMBER | FALSE |
| complete | All shipments for this location are complete. | BOOLEAN | TRUE |
| [shipFromAddress](#address-object) | Ship-From address | OBJECT | TRUE |
| [shipToAddress](#address-object) | Ship-To address | OBJECT | TRUE |
| shipmentDestinationType | Used to identify the type of destination for the Ship-To address. | 64 STRING: Enumerated | FALSE |
| [PackageArray](#package) | An array of shipment packages.7 | ARRAY | FALSE |
## **Address Object**
| Field | Description | Data Type | Required? |
|-------------|---------------------|------------|-----------|
| address1 | Address Line 1 | 64 STRING | TRUE |
| address2 | Address Line 2 | 64 STRING | FALSE |
| address3 | Address Line 3 | 64 STRING | FALSE |
| address4 | Address Line 4 | 64 STRING | FALSE |
| city | Address City | 64 STRING | TRUE |
| region | Address Region | 2 STRING | TRUE |
| postalCode | Address Postal Code | 10 STRING | TRUE |
| country | Address Country | 128 STRING | FALSE |
## **Package**
| Field | Description | Data Type | Required? |
|------------------------ |----------------------------------------------------------|----------------------|-----------|
| id | The id of the package | NUMBER | FALSE |
| trackingNumber | The tracking number for the package | 128 STRING | TRUE |
| shipmentDate | The date for the shipment in UTC.8 | ISO 8601 DATE | TRUE |
| dimUOM | The dimensional unit of measure | 64 STRING: Enumerated| FALSE |
| length | The length of the package | NUMBER | FALSE |
| width | The width of the package | NUMBER | FALSE |
| height | The height of the package | NUMBER | FALSE |
| weightUOM | The weight unit of measure | 64 STRING: Enumerated| FALSE |
| weight | The weight of the package | NUMBER | FALSE |
| carrier | The carrier delivering the package | 128 STRING | FALSE |
| shipmentMethod | The method used for shipping (e.g., Ground) | 128 STRING | FALSE |
| shippingAccount | The shipping account used for this shipment | 128 STRING | FALSE |
| shipmentTerms | The terms of the shipment | 128 STRING | FALSE |
| [ItemArray](#itemarray) | A list of items and quantities contained in the package9 | ARRAY | FALSE |
## **ItemArray**
### **Item Object:**
A list of items and quantities contained in the package.
| Field | Description | Data Type | Required? |
|-------------------------|-----------------------------------------------------|------------|-----------|
| supplierProductId | The supplier product Id | 64 STRING | FALSE |
| supplierPartId | The supplier part Id associated with the supplier product Id | 64 STRING | FALSE |
| distributorProductId | The distributor product Id | 64 STRING | FALSE |
| distributorPartId | The distributor part Id associated with the supplier product Id | 64 STRING | FALSE |
| purchaseOrderLineNumber | The line number of the item on the purchase order | INTEGER | FALSE |
| quantity | The quantity of the item | NUMBER | FALSE |
## **ErrorMessage & Standardized Codes**
#### **ErrorMessage Details:**
| Field | Description | Data Type | Required? |
|-----------------------|---------------------------------------------------|-----------|-----------|
| code | The numerical value of the code | NUMBER | YES |
| description | Response for any error requiring notification to requestor | 256 STRING | YES |
**Standardized Codes**: The range of `100-199` has been reserved for standardized error codes. The number `999` has been reserved for an error codes that is a “General Error - Contact System Service Provider”.
| Code | Description |
|------|-----------------------------------------------------------------------------------------------|
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 999 | General Error – Contact the System Service Provider |
## Service Specific Code:
These error codes are only for the **Order Ship Notification Service**
| Code | Description |
|------|-----------------------------------------------------------------------------------------------|
| 300 | queryType not found |
| 301 | referenceNumber not found |
| 302 | shipmentDateTimeStamp is incorrect or is an invalid date range |
## ENUMERATED TYPE RESTRICTIONS
| Field | Description | Data Type | Values |
|------------------------|-------------------------------------------------------------------|-----------|--------|
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | Enumerated: 1.0.0 |
| shipmentDestinationType| Used to identify the type of destination for the Ship-To address. | 64 STRING | Enumerated: Commercial,Residential, None |
| dimUOM | The dimensional unit of measure | 64 STRING | Enumerated: Inches, Feet, mm, cm, Meters |
| weightUOM | The weight unit of measure | 64 STRING | Enumerated: Ounces, Pounds, Grams, KG |
## Code Samples
To be published under separate specification: “Order Shipment Notification Standards Use Examples”.
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of
the “Interface Data Use Guidelines Standards”.
### Diagrams
### getOrderShipmentNotification Function
```mermaid
classDiagram
class GetOrderShipmentNotificationRequest {
+String wsVersion
+String id
+String password
+String queryType
+String referenceNumber
+ISO 8601 shipmentDateTimeStamp
}
```
```mermaid
classDiagram
class GetOrderShipmentNotificationResponse {
+OrderShipmentNotificationArray orderShipmentNotificationArray
+ErrorMessage errorMessage
}
class OrderShipmentNotification {
+String purchaseOrderNumber
+Boolean complete
+SalesOrderArray salesOrderArray
+ShipmentLocationArray shipmentLocationArray
}
class SalesOrder {
+String salesOrderNumber
+Boolean complete
}
class ShipmentLocation {
+String id
+Boolean complete
+Address shipFromAddress
+Address shipToAddress
+String shipmentDestinationType
+PackageArray packageArray
}
class Address {
+String address1
+String address2
+String address3
+String city
+String region
+String postalCode
+String country
}
class Package {
+String id
+String trackingNumber
+ISO 8601 shipmentDate
+String dimUOM
+Number length
+Number width
+Number height
+String weightUOM
+Number weight
+String carrier
+String shipmentMethod
+String shippingAccount
+String shipmentTerms
+ItemArray itemArray
}
class Item {
+String supplierProductId
+String supplierPartId
+String distributorProductId
+String distributorPartId
+Integer purchaseOrderLineNumber
+Number quantity
}
class ErrorMessage {
+Number code
+String description
}
GetOrderShipmentNotificationResponse --> OrderShipmentNotification : contains
OrderShipmentNotification --> SalesOrder : contains
OrderShipmentNotification --> ShipmentLocation : contains
ShipmentLocation --> Address : contains
ShipmentLocation --> Package : contains
Package --> Item : contains
```
---
## Order Shipment Notification 2.0.0
Source: https://docs.psrestful.com/standards/order-shipment-notification-2.0.0
# Order Shipment Notification 2.0.0
**PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES**

Date: 12/23/2022
## Document Change Log
| Version | Date | Reason for Change | Authors |
| --- | --- | --- | --- |
| 2.0.0 | 2022-12-23 | Version 2.0.0 Final | Stephen Luisser - Essent Corporation |
## Contributors
The following have contributed to the creation of this specification:
Authors:
- Stephen Luisser, Vice President, Essent Corporation
Contributors: The PromoStandards Standards Committee
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface. Additionally, this document will provide sample code in order to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at [http://www.w3.org/TR/soap12-part1/](http://www.w3.org/TR/soap12-part1/).
## Service Details
**Function** : getOrderShipmentNotification()
This function provides a mechanism to get shipment details by specific parameters like (purchase order number, sales order number, or shipment date). This allows the consumer of the service to obtain shipment information grouped by purchase order number and sales order number for their needs.
**Request:** GetOrderShipmentNotificationRequest
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | TRUE |
| id | The customerId or any other agreed upon Id | 64 STRING | TRUE |
| password | The password associated with the customerId | 64 STRING | FALSE |
| queryType | The type of query you wish to perform | FACET INTEGER | TRUE |
| referenceNumber | The purchase order number for queryType =1 or the sales order number for queryType = 2 | 64 STRING | FALSE |
| shipmentDateTimestamp | The date for of shipments to return in UTC. Required when the queryType is 3. ISO 8601 | DATE | FALSE |
**GetOrderShipmentNotificationRequest queryType**
| **queryType** | **Short Name** | **Description** |
| --- | --- | --- |
| 1 | PO Number Search | Returns all shipments for the Purchase Order |
| 2 | SO Number Search | Returns all shipments for the Sales Order |
| 3 | Shipment Date Search | Returns all shipments with a shipment date specified by the shipmentDateTimeStamp |
**Reply:** GetOrderShipmentNotificationResponse
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| [OrderShipmentNotificationArray](#ordershipmentnotification-object) | An array of Order Shipment Notification objects | ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects | OBJECT | FALSE |
### OrderShipmentNotification Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| purchaseOrderNumber | The associated purchase order | 64 STRING | TRUE |
| purchaseOrderShippedInFull | All shipments for this purchase order are complete | BOOLEAN | TRUE |
| [SalesOrderArray](#salesorder-object) | An array of Sales Order objects associated with the purchase order | ARRAY | TRUE |
### SalesOrder Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| salesOrderNumber | The associated sales order | 64 STRING | TRUE |
| salesOrderShippedInFull | All shipments for this sales order are complete | BOOLEAN | TRUE |
| [ShipmentArray](#shipment-object) | An array of Shipment objects | ARRAY | TRUE |
### Shipment Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| destinationShippedInFull | All shipments for this shipping destination location are complete | BOOLEAN | TRUE |
| customerPickup | The shipment will be a pickup and will not be shipped. Default to false if pickup cannot be determined | BOOLEAN | TRUE |
| [shipFromAddress](#shippingcontactdetails-object) | ShippingContactDetails Object for the Ship-From address | OBJECT | TRUE |
| [shipToAddress](#shippingcontactdetails-object) | ShippingContactDetails Object for the Ship-To address | OBJECT | TRUE |
| shipmentDestinationType | Used to identify the type of destination for the Ship-To address | FACET 64 STRING | FALSE |
| [PackageArray](#package-object) | An array of Package objects | ARRAY | TRUE |
### ShippingContactDetails Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| attentionTo | Attention To (first and last name of contact) | 35 STRING | FALSE |
| companyName | Company Name | 35 STRING | FALSE |
| address1 | Address line 1 | 35 STRING | TRUE |
| address2 | Address line 2 | 35 STRING | FALSE |
| address3 | Address line 3 | 35 STRING | FALSE |
| city | The city | 30 STRING | TRUE |
| region | The 2 character US state abbreviation or 2-3 character non-US region | 3 STRING | TRUE |
| postalCode | The postal code | 10 STRING | TRUE |
| country | The country in ISO 3166-2 format | 2 STRING | FALSE |
| email | Email address associated with the contact | 128 STRING | FALSE |
| phone | The phone number associated with the contact | 32 STRING | FALSE |
### Package Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| id | The id of the package | NUMBER | FALSE |
| trackingNumber | The tracking number for the package. Guidance is to always provide a tracking number if available | 128 STRING | FALSE |
| shipmentDate | The date for the shipment in UTC. ISO 8601 | ISO 8601 | TRUE |
| [Dimension](#dimension-object) | Physical dimensions and weight. Include this object if you have either a size or weight dimension | OBJECT | FALSE |
| [FreightDetails](#freightdetails-object) | The details on the freight: carrier and service | OBJECT | FALSE |
| shippingAccount | The shipping account used for this shipment | 128 STRING | FALSE |
| shipmentTerms | The terms of the shipment (i.e. Prepaid, Collect, Third Party, PPD., etc.) | 128 STRING | FALSE |
| [ItemArray](#item-object) | A list of items and quantities contained in the package | ARRAY | FALSE |
| preProductionProof | Signifies if the order is a pre-production proof. Valid options: "Yes", "No", "Unknown" | FACET 64 STRING | TRUE |
### Dimension Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| [DimensionSize](#dimensionsize-object) | Physical dimensions. Include only if you have at least one physical dimension | OBJECT | FALSE |
| [DimensionWeight](#dimensionweight-object) | Weight | OBJECT | FALSE |
### DimensionSize Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| dimensionUom | The dimensional unit of measure | FACET 2 STRING | TRUE |
| length | The length of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| width | The width of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| height | The height of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
### DimensionWeight Object
| **Field** | **Description** | **Data Type** | **Required** |
| --------- | --- | --- | --- |
| weightUom | The weight unit of measure for the part | FACET 2 STRING | TRUE |
| weight | The weight of the part using the unit of measure specified in weightUom | DECIMAL | TRUE |
### FreightDetails Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| carrier | The carrier name of the shipping vendor being requested. (i.e. "UPS", "FEDEX", etc.) | 64 STRING | TRUE |
| service | The service code of the shipping vendor for the service being requested. (i.e. GROUND, 2DAY, NEXTDAY, etc.) | 64 STRING | TRUE |
### Item Object
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| supplierProductId | The supplier product Id | 64 STRING | TRUE |
| supplierPartId | The supplier part Id associated to the supplier product Id | 64 STRING | FALSE |
| distributorProductId | The distributor product Id | 64 STRING | FALSE |
| distributorPartId | The distributor part Id associated to the supplier product Id | 64 STRING | FALSE |
| purchaseOrderLineNumber | The line number of the item on the purchase order | INTEGER | FALSE |
| [Quantity](#quantity-object) | The quantity of the item shipped | OBJECT | TRUE |
### Quantity Object
| **Field** | **Description** | **Data Type** | **Required** |
| --------- | --- | --- | --- |
| value | The quantity value | DECIMAL | TRUE |
| uom | The unit of measure for the value | FACET 2 STRING | TRUE |
**Guidance**
If you are unable to determine the quantity of items shipped by tracking number for a shipment due to process or system limitations, PromoStandards guidance is to attach all products and quantities to a single tracking number and leave the other tracking numbers without products or quantities.
**Enumerated Type Restrictions (FACET)**
| **Field** | **Description** | **Data Type** | **Enumeration Values** |
| --- | --- | --- | --- |
| dimUOM | The dimensional unit of measure | FACET 64 STRING | Inches
Feet
mm
cm
Meters |
| preProductionProofType | Identifies if the item is a pre-production proof | FACET 64 STRING | Yes
No
Unknown |
| quantityUOM | The unit of measure for quantity based field | FACET 2 STRING | BX (Box)CA (Case)DZ (Dozen)EA (Each)KT (Kit)PK (Package)PR (Pair)RL (Roll)ST (Set)SL (Sleeve)TH (Thousand) |
| serviceMessageSeverity | The severity of the message. | FACET 64 STRING | Error
Information
Warning |
| shipmentDestinationType | The type of shipment destination | FACET 64 STRING | Commercial
Residential
Other |
| weightUOM | The weight unit of measure | FACET 64 STRING | Ounces
Pounds
Grams
KG |
## **Appendix A: Service Messages**
**ServiceMessage Object**
| **Field** | **Description** | **Data Type** | **Required** |
| --- | --- | --- | --- |
| code | The numerical value of the code | INTEGER | TRUE |
| description | Response for any message requiring notification to requestor | 256 STRING | TRUE |
| severity | The severity of the message. Values are enumerated: \{Error, Information, Warning\} | FACET 64 STRING | TRUE |
**Standardized Codes**
The range of 100-199 has been reserved for standardized error codes. The number 999 has been reserved for an error codes that is a "General Error - Contact System Service Provider"
| **Code** | **Description** |
| --- | --- |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 999 | General Error – Contact the System Service Provider |
**Service Specific Codes**
These error codes are only for this service.
| **Code** | **Description** |
| --- | --- |
| 300 | queryType not found |
| 301 | referenceNumber not found |
| 302 | shipmentDateTimeStamp is incorrect or is an invalid date range |
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of the "Interface Data Use Guidelines Standards".
**Validation of Services**
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by using the web service validation tool located at: [https://services.promostandards.org/webserviceValidator/home](https://services.promostandards.org/webserviceValidator/home).
Select the service, version, method and input your endpoint. If the endpoint is correct, you should receive a message of: "The XML response is valid."
## Diagrams
### getOrderShipmentNotification Function
```mermaid
classDiagram
class GetOrderShipmentNotificationRequest {
+String wsVersion
+String id
+String password
+FacetInteger queryType
+String referenceNumber
+Date shipmentDateTimestamp
}
class GetOrderShipmentNotificationResponse {
+OrderShipmentNotificationArray orderShipmentNotificationArray
+ServiceMessageArray serviceMessageArray
}
class OrderShipmentNotificationArray {
+OrderShipmentNotification[] orderShipmentNotifications
}
class ServiceMessageArray {
+ServiceMessage[] serviceMessages
}
class OrderShipmentNotification {
+String purchaseOrderNumber
+Boolean purchaseOrderShippedInFull
+SalesOrderArray salesOrderArray
}
class SalesOrderArray {
+SalesOrder[] salesOrders
}
class SalesOrder {
+String salesOrderNumber
+Boolean salesOrderShippedInFull
+ShipmentArray shipmentArray
}
class ShipmentArray {
+Shipment[] shipments
}
class Shipment {
+Boolean destinationShippedInFull
+Boolean customerPickup
+ShippingContactDetails shipFromAddress
+ShippingContactDetails shipToAddress
+FacetString shipmentDestinationType
+PackageArray packageArray
}
class ShippingContactDetails {
+String attentionTo
+String companyName
+String address1
+String city
+String region
+String postalCode
+String country
+String email
+String phone
}
class PackageArray {
+Package[] packages
}
class Package {
+Integer id
+String trackingNumber
+Date shipmentDate
+Dimension dimension
+FreightDetails freightDetails
+String shippingAccount
+String shipmentTerms
+ItemArray itemArray
+FacetString preProductionProof
}
class Dimension {
+DimensionSize dimensionSize
+DimensionWeight dimensionWeight
}
class FreightDetails {
+String carrier
+String service
}
class ItemArray {
+Item[] items
}
class Item {
+String supplierProductId
+String supplierPartId
+String distributorProductId
+String distributorPartId
+Integer purchaseOrderLineNumber
+Quantity quantity
}
class Quantity {
+Decimal value
+FacetString uom
}
GetOrderShipmentNotificationRequest --> GetOrderShipmentNotificationResponse : requests
GetOrderShipmentNotificationResponse --> OrderShipmentNotificationArray : contains
GetOrderShipmentNotificationResponse --> ServiceMessageArray : contains
OrderShipmentNotificationArray --> OrderShipmentNotification : consists of
OrderShipmentNotification --> SalesOrderArray : consists of
SalesOrderArray --> SalesOrder : consists of
SalesOrder --> ShipmentArray : consists of
ShipmentArray --> Shipment : consists of
Shipment --> PackageArray : consists of
PackageArray --> Package : consists of
Package --> ItemArray : consists of
ItemArray --> Item : consists of
```
---
## Order Status 1.0.0
Source: https://docs.psrestful.com/standards/order-status-1.0.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

## Order Status 1.0.0
**Version**: 1.0.0
**Release Date**: 2015-07-01
## **Document Change Log**
| Version | Date | Reason for Change | Author |
|---------|------------|----------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
| 0.0.1 | 2014-05-12 | Initial Draft | Jon Norris, System Operations Manager, Starline USA |
| 0.0.2 | 2014-05-13 | - Changed Order Status Detail into multidimensional array to accommodate multiple sales orders to one purchase order. - Added Credit Hold and Backorder Statuses. - Added In Hands Date Field - Added Ship Method | Eric Shonebarger, CIO, Hit Promotional Products Inc |
| 0.0.3 | 2014-05-30 | - Added support for requesting single purchase order or array of purchase orders based on date - Added statusTimeStampto request - Changed function name: getOrderStatusDetails() - Fixing array naming to OrderStatusDetailArray - Created function getOrderStatusTypes() | Jon Norris, System Operations Manager, Starline USA |
| 0.0.4 | 2014-07-07 | - Added unique request and response names for both methods - Fixed Required Flag on expectedInHandsDate | Jon Norris, System Operations Manager, Starline USA |
| 0.0.5 | 2015-04-13 | - Changed purchaseOrder references to referenceNumber (more generic) - Changed to more rigid search parameters and warned everyone to account for time jitter - Changed Search parameter to UTC - Changed Status Codes to reflect desire for logical progression and to conform to traditional 855/997 documents | Eric Shonebarger, CIO, Hit Promotional Products, Inc |
| 0.0.8 | | - Made some adjustments to status codes in order to facilitate adoption as well as their descriptions - Changed “inHandsDate” to “expectedDeliveryDate” and adjusted description to be more descriptive - Adjusted factoryOrderNumber description - Made the ResponseToArray description a little more descriptive - Adjusted customMessage to 1024 - Added the boolean responseRequired to determine if a status requires a response from the distributor - Made the “Custom Message” more descriptive | Eric Shonebarger, CIO, Hit Promotional Products, Inc |
| 0.0.9 | | - Updated explanation for purchase order naming. - Renamed custom message to “additionalExplanation” - Changed statusID data type to Integer (Enumeration) from Number | Jon Norris, VP Operations, Starline |
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface. Additionally, this document will provide sample code in order to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at [w3](http://www.w3.org/TR/soap12-part1/)
## **Service Details:**
- [getOrderStatusDetails](#getorderstatusdetails)
- [getOrderStatusTypes](#getorderstatustypes)
## getOrderStatusDetails
### **Request:**
#### **GetOrderStatusDetailsRequest**
| Field | Description | Data Type | Required? |
|------------------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | TRUE |
| id | The customerID or any other agreed upon ID. | 64 STRING | TRUE |
| password | The password associated with the customerID. | 64 STRING | FALSE |
| queryType | The type of query you wish to perform[^4] | INT | TRUE |
| referenceNumber | The purchase order/sales order associated with the order. | 64 STRING | FALSE |
| statusTimeStamp | Beginning date time since last status change in UTC[^5] | ISO 8601 | FALSE |
[^4]: This indicates the type of query you are trying to perform. Valid Options:
| queryType | Short Name | Description |
|-----------|--------------------|-----------------------------------------------------------------------------------------------|
| 1 | PO Search | Query based on customer provided purchase order number. |
| 2 | SO Search | Query based on vendor assigned sales order number |
| 3 | Last Update Search | Query based on all orders with an update time greater than the value specified in statusTimeStamp. |
| 4 | All Open Search | Query based on all orders that currently have a status other than “Complete” and “Cancelled” |
[^5]: This field will indicate the change date time for any status change GREATER than the date time stamp provided. Note: Make sure to account for some jitter between the two systems and make sure to specify your time request in UTC and not in the local time zone.
### **Reply:**
#### **GetOrderStatusDetailsResponse**
| Field | Description | Data Type | Required? |
|---------------------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| [OrderStatusArray[^1]](#orderstatus-object)| An array of order status | ARRAY | TRUE |
| errorMessage | Response for any error requiring notification to requestor | 256 STRING| FALSE |
[^1]: **OrderStatusArray**
## OrderStatus Object
| Field | Description | Data Type | Required? |
|--------------------------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| purchaseOrderNumber | The associated Purchase Order Number from the customer | 64 STRING | TRUE |
| [OrderStatusDetailArray[^2]](#orderstatusdetail-object)| Array of OrderStatusDetails | ARRAY | TRUE |
[^2]: **OrderStatusDetailArray**
## OrderStatusDetail Object
| Field | Description | Data Type | Required? |
|----------------------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| factoryOrderNumber | The associated factory sales order number (This is used in the example of internal PO splitting at the factory level) | 64 STRING | TRUE |
| statusID[^5] | Paired standard order status from allowed values | Integer | TRUE |
| statusName[^5] | Textual description of statusID | 256 STRING| TRUE |
| expectedShipDate | The expected ship date for the purchase order | ISO 8601 | FALSE |
| expectedDeliveryDate | The expected date the order should arrive at customer also known as the “in hands date” | ISO 8601 | FALSE |
| [ResponseToArray[^4]](#response-object) | An array of human contact information such as email, phone, and rep name so the distributor knows who to get in contact to fix an issue. | ARRAY | FALSE |
| additionalExplanation[^3] | This allows further detail about the status | 1024 STRING | FALSE |
| responseRequired | True or False to determine if it is necessary to provide information back to the supplier to complete the order | Boolean | TRUE |
| validTimestamp | Time of order status | ISO 8601 | TRUE |
[^3]: **customMessage Examples:**
- If the Order Status is 43 (Artwork Hold), The custom message could be “Size of Text is too small.”
- If the Order Status is 11 (Order Entry Hold) The custom message could be “We can’t meet your delivery date with the ship method provide.”
Using additionalExplanation in conjunction with the ResponseToArray will point the distributor who to contact to fix the issue.
[^4]: **ResponseToArray**
## ResponseTo Object
| Field | Description | Data Type | Required? |
|--------------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| name | The name of the person to respond to | 256 STRING| FALSE |
| emailAddress | A monitored email address that a recipient can send their response to | 256 STRING| FALSE |
| phoneNumber | The phone number of the person to respond | 256 STRING| FALSE |
## getOrderStatusTypes
#### **GetOrderStatusTypesRequest**
| Field | Description | Data Type | Required? |
|-----------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | TRUE |
| id | The customerID or any other agreed upon ID. | 64 STRING | TRUE |
| password | The password associated with the customerID. | 64 STRING | FALSE |
### **Reply:**
#### **GetOrderStatusTypes Response**
| Field | Description | Data Type | Required? |
|-------------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| [StatusArray[^4]](#status) | An array of order status | ARRAY | TRUE |
| errorMessage | Response for any error requiring notification to requestor | 256 STRING| FALSE |
[^4]: **StatusArray**
### Status
| Field | Description | Data Type | Required? |
|-------|---------------------------------------------------------------------------------------------------|-----------|-----------|
| id | The numerical value of the order status | NUMBER | TRUE |
| name | The string status name from possible values[^5] | 256 STRING| TRUE |
[^5]: **Possible Values for Status**
| StatusID | Status Name | Description |
|----------|------------------|---------------------------------------------------------------------------------------------------|
| 10 | Order Received | Order has been received. |
| 11 | Order Entry Hold | Vendor has a problem with the data in the purchase order, and it is preventing the order from being entered. |
| 20 | Order Confirmed | Order has been received, entered, and accepted |
| 30 | Pre-Production | Vendor has begun to process the order, but it is not in production |
| 40 | General Hold | Something is preventing the order from being entered |
| 41 | Credit Hold | Vendor is awaiting payment from customer |
| 42 | Proof Hold | Vendor is awaiting response to proof |
| 43 | Art Hold | Vendor is awaiting suitable artwork from customer |
| 44 | Back Order Hold | Order has been backordered; Nothing has shipped yet. |
| 60 | In Production | Production of the order has started |
| 70 | In Storage | Order is complete, but vendor is waiting to ship goods |
| 75 | Partial Shipment | Order has shipped in Part; remaining items in production |
| 80 | Complete | Order has shipped in full—No further updates will be given |
| 99 | Canceled | Order has been canceled—No further updates will be given |
## Code Samples
To be published under separate specification: “Inventory Interface Standards Use Examples”
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of the “Interface Data Use Guidelines Standards”
### Validation of Services
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by using the web service validation tool.
http://services.promostandards.org/webserviceValidator/home
Select the service, version, method and input your endpoint. If the endpoint is correct you should receive a message of:
"The XML response is valid."
## Diagrams
### getOrderStatusDetails Function
```mermaid
classDiagram
class GetOrderStatusDetailsRequest {
+String wsVersion
+String id
+String password
+Int queryType
+String referenceNumber
+ISO 8601 statusTimeStamp
}
class GetOrderStatusDetailsResponse {
+OrderStatusArray orderStatusArray
+String errorMessage
}
class OrderStatus {
+String purchaseOrderNumber
+OrderStatusDetailArray orderStatusDetailArray
}
class OrderStatusDetail {
+String factoryOrderNumber
+Integer statusID
+String statusName
+ISO 8601 expectedShipDate
+ISO 8601 expectedDeliveryDate
+ResponseToArray responseToArray
+String additionalExplanation
+Boolean responseRequired
+ISO 8601 validTimestamp
}
class ResponseObject {
+String name
+String emailAddress
+String phoneNumber
}
GetOrderStatusDetailsRequest --> GetOrderStatusDetailsResponse : returns
GetOrderStatusDetailsResponse --> OrderStatus : contains
OrderStatus --> OrderStatusDetail : contains
OrderStatusDetail --> ResponseObject : contains
```
### getOrderStatusTypes Function
```mermaid
classDiagram
class GetOrderStatusTypesRequest {
+String wsVersion
+String id
+String password
}
class GetOrderStatusTypesResponse {
+StatusArray statusArray
+String errorMessage
}
class Status {
+Number id
+String name
}
GetOrderStatusTypesRequest --> GetOrderStatusTypesResponse : returns
GetOrderStatusTypesResponse --> Status : contains
```
---
## Order Status 2.0.0
Source: https://docs.psrestful.com/standards/order-status-2.0.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

#### Promotional Products Data Interface Specification for Web services
## Order Status 2.0.0
Date: 5/18/2023
## Document Change Log
| Version | Date | Reason for Change | Authors |
| --- | --- | --- | --- |
| 2.0.0 | 2023-05-18 | Version 2.0.0 Final | Stephen Luisser - Hit Promotional ProductsRaj Mukherjee – Hit Promotional Products |
## Contributors
The following have contributed to the creation of this specification:
Contributors:
- The PromoStandards Standards Committee
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface.
This document assumes that the reader is fluent in web based technologies and has knowledge of the programming language they plan to create or consume the web service with.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at [http://www.w3.org/TR/soap12-part1/](http://www.w3.org/TR/soap12-part1/).
## Service Details
Function: getOrderStatus()
This function provides a mechanism to get order status by specific parameters such as purchase order number, sales order number, or transaction id. This allows the consumer of the service to obtain order status information grouped by purchase order number and sales order number for their needs. This function is required to implement.
### Request: GetOrderStatusRequest
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | TRUE |
| id | The id of the entity making the request or any other agreed upon Id | 64 STRING | TRUE |
| password | The password associated with the customerId | 64 STRING | FALSE |
| queryType | The type of query you wish to perform, see the queryType facet for acceptable values | FACET STRING | TRUE |
| referenceNumber | The purchase order number for queryType poSearch, the sales order number for queryType soSearch, or the transaction id for queryType transactionId. Required when any of these queryTypes are specified. | 64 STRING | FALSE |
| statusTimeStamp | Beginning datetime since last status change in UTC. This allows an entity to request status for any orders which have been updated since this datetime. Make sure to account for some jitter between the two systems and make sure to specify your time request in UTC and not in the local time zone. Required with queryType lastUpdate. | DATETIME | FALSE |
| returnIssueDetailType | Returns detailed information regarding order issues including recommended actions to resolve open issues if available when set to TRUE. See the returnIssueDetailsType facet for acceptable values. When this parameter is not included, the noIssues option is used. | FACET STRING | FALSE |
| returnProductDetail | Returns detailed information regarding the order/line item information when set to TRUE. The default value FALSE is used when this parameter is not included. | BOOLEAN | FALSE |
### GetOrderStatusRequest queryType
| Value | Short Name | Description |
| ------------- | -------------------- | ---------------------------------------------------------- |
| poSearch | PO Search | Query based on the Customer provided Purchase Order Number |
| soSearch | SO Search | Query based on the Customer provided Sales Order Number |
| lastUpdate | Last Update Search | Query based on all orders with an updated time greater than the value specified in the statusTimeStamp field |
| allOpen | All Open Search | Query based on all Sales Orders that currently have a Status other than Complete or Canceled |
| allOpenIssues | All Open Issues | Query based on all open orders that currently have an issue associated with them with an Issue Status of Open or Pending |
| transactionId | transactionId Search | Query based on the transactionId returned when submitting a Purchase Order to the PromoStandards Purchase Order service. This queryType is not applicable for Purchase Orders submitted another way. This is useful to check up on a Purchase Order that is not yet entered in the Suppliers Order Management system. This option may not be supported by all Suppliers and it is not to replace the PO Search option. If this queryType is not supported return error 210, Query Type not supported. |
### GetOrderStatusRequest returnIssueDetailType
| Value | Short Name | Description |
| ---------- | ----------- | --------------------------------------------- |
| noIssues | No Issues | The response does not include issues details. |
| openIssues | Open Issues | If supported, the response includes issues details for issues with an Issue Status of Open or Pending. |
| allIssues | All Issues | If supported, the response includes issues details for all issues. |
### Reply: GetOrderStatusResponse
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| [OrderStatusArray](#orderstatus-object) | An array of OrderStatus objects | ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects | ARRAY | FALSE |
### OrderStatus Object
| Field | Description | Data Type | Required |
| ------------------- | ----------------------------- | --------- | -------- |
| purchaseOrderNumber | The associated purchase order | 64 STRING | TRUE |
| [OrderStatusDetailArray](#orderstatusdetail-object) | An array of OrderStatusDetail objects for all sale orders associated with the purchaseOrderNumber even in the case where the queryType is lastUpdate, allOpen, allOpenIssues. | ARRAY | TRUE |
| auditURL | The URL to a page that can be used to find additional details about the Purchase Order. This page may include the XML posted to the PromoStandards Purchase Order service, a visual representation of the Purchase Order, a visual representation of the Invoice, or other relevant information. | 1024 STRING | FALSE |
### OrderStatusDetail Object
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| salesOrderNumber | The associated sales order. For Purchase Orders that do not have a Sales Order in the Supplier Order Management system yet, use the value N/A. | 64 STRING | TRUE |
| status | Status of the sales order, see the status facet for acceptable values | FACET 64 STRING | TRUE |
| issueCategory | The category of the issue that is placing or will place the order on hold, see the issueCategory facet for acceptable values | FACET 64 STRING | FALSE |
| expectedShipDate | The expected date the order should ship in the shipper's timezone. This field does not include a time component. | DATE | FALSE |
| expectedDeliveryDate | The expected date the order should arrive at the destination, also known as the _in hands date_. This field does not include a time component. | DATE | FALSE |
| additionalExplanation | A freeform text field which can include additional details about the status of the sales order | 1024 STRING | FALSE |
| qualityProofURL | A URL of the product with logo | 1024 STRING | FALSE |
| [OrderContactArray](#contact-object) | An array of Contact objects. Enables the consumer to know whom to contact about the order. | ARRAY | FALSE |
| [ProductArray](#product-object) | An array of Product objects. This field should be populated if returnProductDetail is true in the GetOrderStatusRequest object | ARRAY | FALSE |
| [IssueArray](#issue-object) | An array of Issue objects. This field should not be populated if returnIssueDetailType is noIssues in the GetOrderStatusRequest object | ARRAY | FALSE |
| validTimestamp | Time in UTC of the last status change for the sales order. This is not the time of the request or response. | DATETIME | TRUE |
### Contact Object
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| accountName | The name of the account for the contact if applicable | 64 STRING | FALSE |
| accountNumber | The number of the account for the contact if applicable | 64 STRING | FALSE |
| contactType | The type of contact, see the contactType facet for acceptable values | FACET 64 STRING | TRUE |
| ContactDetails | A ContactDetails object which contains the details about the contact | OBJECT | TRUE |
### ContactDetails Object
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| attentionTo | Attention To (first and last name of contact, department, or role) | 35 STRING | FALSE |
| companyName | The Company name for the contact. When dealing with a conglomerate or company with multiple divisions, the account name in the Contact Object may differ from the value specified here. | 35 STRING | FALSE |
| address1 | Address line 1 | 35 STRING | FALSE |
| address2 | Address line 2 | 35 STRING | FALSE |
| address3 | Address line 3 | 35 STRING | FALSE |
| city | The city | 30 STRING | FALSE |
| region | The 2 character US state abbreviation or 2-3 character non-US region | 3 STRING | FALSE |
| postalCode | The postal code | 10 STRING | FALSE |
| country | The country in ISO 3166-2 format | 2 STRING | FALSE |
| email | Email address associated with the contact | 128 STRING | FALSE |
| phone | The phone number associated with the contact | 32 STRING | FALSE |
| comments | Comments regarding the contact for further clarification, use only when absolutely necessary | STRING | FALSE |
### Product Object
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| productId | The supplier product Id associated to the Order status and supplier sales order line item | 64 STRING | TRUE |
| partId | The supplier part Id associated to the Order Status and supplier sales order line item | 64 STRING | FALSE |
| salesOrderLineNumber | The supplier sales order line number | 64 STRING | TRUE |
| purchaseOrderLineNumber | The line number on the purchase order supplied by the buyer | 64 STRING | FALSE |
| QuantityOrdered | A Quantity object, the ordered quantity of the product for the sales order line number | OBJECT | TRUE |
| QuantityShipped | A Quantity object, the shipped quantity of the product for the sales order line number | OBJECT | FALSE |
| issueCategory | The category of the issue that is placing or will place the product for the SO on hold, see the issueCategory facet for acceptable values | FACET 64 STRING | FALSE |
| status | Status of the product for the SO, see the status facet for acceptable values | FACET 64 STRING | TRUE |
### Quantity Object
| Field | Description | Data Type | Required |
| ----- | --- | --- | --- |
| value | The quantity value | DECIMAL | TRUE |
| uom | The unit of measure for the value, see the quantityUOM facet for acceptable values | FACET 2 STRING | TRUE |
### Issue Object
| Field | Description | Data Type | Required |
| ---------------------- | ----------- | --------- | -------- |
| issueId | A unique identifier to retrieve the details behind the issue. The issueId is used in conjunction with the getIssue method. | 64 STRING | FALSE |
| issueStatus | The status of the issue, see the issueStatus facet for acceptable values | FACET 64 STRING | TRUE |
| issueCategory | The category of the issue that is placing or will place the order on hold, see the issueCategory facet for acceptable values | FACET 64 STRING | TRUE |
| issueName | The name of the issue which should provide more specific information than issueCategory and less specific than issueDescription. If no additional information is available use the value from the issueCategory field. | 64 STRING | FALSE |
| urgentResponseRequired | Indicates that the order is currently on hold pending information and order is blocked from progressing to completion | BOOLEAN | TRUE |
| issueDescription | A detailed description of the issue | 1024 STRING | FALSE |
| responseRequiredBy | The date/time in UTC that the issue needs to be resolved by in order to not delay the order. | ISO 8601 | FALSE |
| issueResolutionURL | A supplier-hosted URL which provides a custom user interface that allows the consumer to resolve the issue | 1024 STRING | FALSE |
| [ResolutionArray](#resolution-object) | An array of Resolution objects | ARRAY | FALSE |
| issueBlockingStatus | The earliest status to which the order cannot progress without resolution, see the status facet for acceptable valuesFor example, without an address the order can be produced (In Production) but cannot ship (Shipped) so the issueBlockingStatus would be "Shipped". | FACET 64 STRING | FALSE |
| [ContactArray](#contact-object) | An array of Contact objects. Enables the consumer to know whom to contact about the order. | ARRAY | FALSE |
| productId | The supplier product Id if the issue is for a specific product. For order level issues this field should not be set. | 64 STRING | FALSE |
1This object currently provides data regarding issues. A future standard, Order Management is currently under development. This object will communicate issues where resolutions can be electronically sent to a supplier and programmatically resolved. The intent of this is to automate customer service-related activities involving orders. As such, the Issues object and its contained objects may undergo revisions in subsequent versions as Order Management is published.
### Resolution Object
| Field | Description | Data Type | Required |
| --------------------- | --------------------------------------------- | ----------- | -------- |
| resolutionId | A unique identifier related to the resolution | 64 STRING | TRUE |
| resolutionName | Short name to describe the resolution | 64 STRING | TRUE |
| resolutionDescription | Detailed description of the resolution | 1024 STRING | TRUE |
| [ParameterArray](#parameter-object) | Parameters to resolve the issue | ARRAY | FALSE |
### Parameter Object
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| parameterId | The identifier for the parameter | 64 STRING | TRUE |
| name | Short name of the parameter | 64 STRING | TRUE |
| type | The type of variable that the parameter represents Recommended to use ANSI SQL Data Types | 64 STRING | FALSE |
| displayOrder | The order in which to display the parameter to the user | INTEGER | FALSE |
| required | Is the parameter required to process the item | BOOLEAN | TRUE |
| length | The length of the parameter | INTEGER | FALSE |
| pspoFieldName | The field name of the parameter name in PromoStandards Purchase Order format | 64 STRING | FALSE |
| pspoVersion | The version of the PromoStandards Purchase Order pspoFieldName is referencing. This must be set if the pspoFieldName is set. | 64 STRING | FALSE |
| [validResponseArray](#validresponse-object) | An array of valid responses for this parameter | OBJECT | FALSE |
### ValidResponse Object
| Field | Description | Data Type | Required |
| ----- | ------------------------------------------ | --------- | -------- |
| value | A valid response for the related parameter | STRING | TRUE |
### ServiceMessage Object
| Field | Description | Data Type | Required |
| ---------- | --- | --- | --- |
| code | The numerical value of the code | INTEGER | TRUE |
| description | Response for any message requiring notification to requestor | 256 STRING | TRUE |
| severity | The severity of the message, see the serviceMessageSeverity facet for acceptable values | FACET 64 STRING | TRUE |
### Function: getIssue()
This function provides a mechanism to get details for an issue. This function is optional to implement.
### Request: GetIssueRequest
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | TRUE |
| id | The customerId or any other agreed upon Id | 64 STRING | TRUE |
| password | The password associated with the customerId | 64 STRING | FALSE |
| issueId | The issueId for which the latest information should be returned | 64 STRING | TRUE |
### Reply: GetIssueResponse
| Field | Description | Data Type | Required |
| ------------------- | ---------------------------------- | --------- | -------- |
| [IssueArray](#issue-object) | An array of Issue objects. | ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects | ARRAY | FALSE |
### Function: getServiceMethods()
This function provides a mechanism to get a methods supported by the implementation of the service. This function is required to implement.
### Request: GetServiceMethodsRequest
| Field | Description | Data Type | Required |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING | TRUE |
| id | The customerId or any other agreed upon Id | 64 STRING | TRUE |
| password | The password associated with the customerId | 64 STRING | FALSE |
### Reply: GetServiceMethodsResponse
| Field | Description | Data Type | Required |
| ------------------- | ---------------------------------- | --------- | -------- |
| [ServiceMethodArray](#servicemethod-object) | An array of ServiceMethod objects | ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects | ARRAY | FALSE |
### ServiceMethod Object
| Field | Description | Data Type | Required |
| ------------- | ---------------------------- | --------- | -------- |
| serviceMethod | The name of a function that the implementation of the service supports. Acceptable values: \{getOrderStatus, getIssue, getServiceMethods\} | 128 STRING | TRUE |
### Guidance
The ContactDetails object should include at a minimum an address, email, or phone.
All functions in this service have an optional array to return data and a ServiceMessageArray. As such, at least one array should be populated in the response.
Fields with the DATETIME data type are to be in UTC using the ISO 8601 format and not include a time zone. An example of a properly formatted ISO 8601 UTC date is 2022-06-14T15:26:32Z
There may be limitations with the amount of data that can be processed or returned with a call to GetOrderStatusRequest. An example is limiting the date search to 60 days. For cases such as this, you can return Service Message 125 - Not Supported with the appropriate severity.
It is recommended that the getOrderStatus function when called by lastUpdate supports going back at least a minimum of 7 days.
For Status at the order and product level. Issues such as a back order should be reported as the status even if the majority of items are not backordered. The worst status should be returned when there are products on the order with different statuses. If the preference is to discretely show backordered products for a purchase order, backordered products can be moved to another sales order.
For scenarios for a Purchase Order using Sales Order Number N/A.
- If the Purchase Order is still being processed or in a manual review, the Status returned should be _Order Received_ and any relevant details should be included in the additionalExplanation field.
- If the Purchase Order has been rejected, the Status returned should be _Canceled_ and any relevant details should be included in the additionalExplanation field.
- If the Purchase Order was not found, even when searching by transactionId, a service message with Code 220, No Orders were found for the requested criteria
Enumerated Type Restrictions (FACET)
### status Facet
| Name | Description | Enumeration Value |
| ----------------- | ------------------------------------------------------------------------------------- | ----------------- |
| Received | Order or line item has been received | received |
| Confirmed | Order or line item has been received, entered, and accepted | confirmed |
| Preproduction | Vendor has begun to process the order, but it is not in production | preproduction |
| In Production | Production of the order has started | inProduction |
| In Storage | Order or line item is complete, but the vendor is waiting to ship goods | inStorage |
| Partially Shipped | Order or line item has shipped in Part; remaining items in production | partiallyShipped |
| Shipped | Order or line item has shipped in full | shipped |
| Complete | Order or line item has shipped in full and invoiced, No further updates will be given | complete |
| Canceled | Order or line item has been canceled, no further updates will be given | canceled |
### issueCategory Facet
| Name | Description | Enumeration Value |
| --- | --- | --- |
| Order Entry Hold | Vendor has a problem with the data in the purchase order, and it is preventing the order from being entered | orderEntryHold |
| General Hold | Something is preventing the order from being entered | generalHold |
| Credit Hold | Vendor is awaiting payment from the customer | creditHold |
| Proof Hold | Vendor is awaiting response to proof | proofHold |
| Art Hold | Vendor is awaiting suitable artwork from the customer | artHold |
| Back Order Hold | Order or line item has been backordered; Nothing has shipped yet | backOrderHold |
| Shipping Hold | Order or line item has been produced and is on hold preventing shipping | shippingHold |
| Customer Supplied Item Hold | Order or line item is on hold waiting for customer owned goods | customerSuppliedItemHold |
### Simple Facets
| Field | Description | Data Type | Enumeration Values |
| ----------- | -------------------------------------------- | --------------- | --------------------------------- |
| contactType | The type of contact | FACET 64 STRING | Art
Bill
Expeditor
Order
Sales
Sold |
| issueStatus | The status of an Issue | FACET 64 STRING | Pending
Open
Closed |
| quantityUOM | The unit of measure for quantity based field | FACET 2 STRING | BX (Box)
CA (Case)
DZ (Dozen)
EA (Each)
KT (Kit)
PK (Package)
PR (Pair)
RL (Roll)
SL (Sleeve)
ST (Set)
TH (Thousand) |
| serviceMessageSeverity | The severity of the message | FACET 64 STRING | Error
Information
Warning |
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current guidelines of the "Interface Data Use Guidelines Standards".
Validation of Services
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by using the web service validation tool located at: [https://www.promostandards.org/PromoStandards-Service-Validator](https://www.promostandards.org/PromoStandards-Service-Validator)
Select the service, version, method and input your endpoint. If the endpoint is correct, you should receive a message of: "The XML response is valid."
## Appendix A: Service Messages
### Standardized Codes
The range of `100-199` has been reserved for standardized error codes. The number 999 has been reserved for an error codes that is a "General Error - Contact System Service Provider"
| Code | Description |
| --- | --- |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 130 | Function not supported |
| 999 | General Error – Contact the System Service Provider |
### Service Specific Codes
These error codes are only for this service.
| Code | Description |
| --- | --- |
| 200 | queryType not found |
| 210 | queryType not supported |
| 212 | Function does not support returning issues
[This message is used when the implementation does not support returning issues.] |
| 213 | returnIssueDetailType not supported
[This message is used when the implementation supports returning issues but not the type requested. For example, allIssues is specified but the function can only return openIssues. |
| 214 | Function does not support returning Product Details |
| 220 | No Orders were found for the requested criteria |
| 230 | No Issue was found for the given issue identifier |
| 240 | The specified Issue has already been resolved |
| 250 | statusTimeStamp is incorrect or is an invalid date range |
## Diagrams
### getOrderStatusDetails Function
```mermaid
classDiagram
class GetOrderStatusRequest {
+String wsVersion
+String id
+String password
+FacetString queryType
+String referenceNumber
+DateTime statusTimeStamp
+FacetString returnIssueDetailType
+Boolean returnProductDetail
}
class GetOrderStatusResponse {
+OrderStatusArray orderStatusArray
+ServiceMessageArray serviceMessageArray
}
class OrderStatusArray {
+OrderStatus[] orderStatuses
}
class ServiceMessageArray {
+ServiceMessage[] serviceMessages
}
class OrderStatus {
+String purchaseOrderNumber
+OrderStatusDetailArray orderStatusDetailArray
+String auditURL
}
class OrderStatusDetailArray {
+OrderStatusDetail[] orderStatusDetails
}
class OrderStatusDetail {
+String salesOrderNumber
+FacetString status
+FacetString issueCategory
+Date expectedShipDate
+Date expectedDeliveryDate
+String additionalExplanation
+String qualityProofURL
+OrderContactArray orderContactArray
+ProductArray productArray
+IssueArray issueArray
+DateTime validTimestamp
}
GetOrderStatusRequest --> GetOrderStatusResponse : requests
GetOrderStatusResponse --> OrderStatusArray : contains
GetOrderStatusResponse --> ServiceMessageArray : contains
OrderStatusArray --> OrderStatus : consists of
OrderStatus --> OrderStatusDetailArray : consists of
OrderStatusDetailArray --> OrderStatusDetail : consists of
```
---
## Standards Overview
Source: https://docs.psrestful.com/standards/overview
# Overview of PromoStandards Services
[PromoStandards.org](https://tools.promostandards.org/) provides a documentation and testing environment for PromoStandards web services. While it is intended to be used by developers who are implementing PromoStandards web services in their applications, it is not very developer friendly. In this article we provide you a better overview to understand the services and how they relate to each other.
## PromoStandards Services for creating a good catalog
There are 3 PromoStandards services that are used to create a good catalog. They are:
- Product Data
- Media Content
- Pricing and Configuration
### Product Data(Product)
Product Data will provide you with the information about the product. This includes the product name, description, and other information about the product. This is the most important service for creating a good catalog. The Product Data service is used to create the product catalog and the product detail pages. It will also provide information about parts, with keywords so you can use them to generate a keyword search functionality on your site.
You will also be able to update your product information based on the `getProductDateModified` function.
But it does not provide the images for the product. For that you need to use the `Media Content` service.
### Media Content(MED)
Media Content will provide you with the media content for the product and parts. This includes the product images, product videos, and other media content like templates for the product. This service is used to create the product detail pages. Unfortunately, this service does not provide all the data you need to create a good product detail page but, usually it is the supplier that doesn't provide a good classification of the image like blank or decorated, or the image is not a good quality image.
You will also be able to update your product media content based on `getMediaContentModified` function.
Now you have the product data and the media content for the product. But you still need the pricing and configuration for the product.
### Pricing and Configuration(PPC)
Pricing and Configuration will provide you with the pricing and configuration for the product.
This includes the pricing for blank or decorated parts. It also tells you all the available charges that are available for this product, all the locations we can print on the product, and all the available colors for the product.
This service is used to create the product detail pages but also it is very important for creating a Configured Purchase Order.
You now have a product with enough information to create a good product detail page. But you still need to know if the product is available for sale.
## Inventory(INV)
Inventory will provide you with the inventory for the product and parts and also give you future availability for the product. You can use it to prevent your users to buy a product that is no longer available or to show the future availability for the product.
## Creating Purchase Orders(PO)
Your customers already know what product to buy, the also know where it needs to be shipped, all the decorations they want. Now it is time to create a purchase order.
The Purchase Order service allows you to buy `Blank`, `Sample` or `Custom Decorated Products`.
Blank and Sample orders are very simple to create. You just need to provide the part information and the quantity, shipping information and contacts for the orders.
For Custom Decorated Products you need to go by `Configured` Purchase Orders. In addition to the information to create a blank order you also need to provide the decoration information. This includes the decoration method, the locations, the colors, the charges, and the artwork.
You were able to place your Purchase Order. Now you need to track it.
## Tracking Purchase Orders using Order Status(ODRSTAT)
This allows the consumer of the service to obtain `order status information` grouped by `purchase order number` and `sales order number` for their needs.
#### The Order Status can be:
- `Order Received`(Order has been received)
- `Order Entry Hold`(Vendor has a problem with the data in the purchase order, and it is preventing the order from being entered.)
- `Order Confirmed`(Order has been received, entered, and accepted)
- `Pre-Production`(Vendor has begun to process the order, but it is not in production)
- `General Hold`(Something is preventing the order from being entered)
- `Credit Hold`(Vendor is awaiting payment from customer)
- `Proof Hold`(Vendor is awaiting response to proof)
- `Art Hold`(Vendor is awaiting suitable artwork from customer)
- `Back Order Hold`(Order has been backordered; Nothing has shipped yet.)
- `In Production`(Production of the order has started)
- `In Storage`(Order is complete, but vendor is waiting to ship goods)
- `Partial Shipment`(Order has shipped in Part; remaining items in production)
- `Complete`(Order has shipped in full—No further updates will be given)
- `Canceled`(Order has been canceled—No further updates will be given)
Once the order is shipped(even partially) you can start tracking the shipment.
## Tracking Shipments using Order Shipment Notification(OSN)
For that you need to use the Order Shipment Notification service. This service will provide you with the tracking information for the order based on parameters like purchase order number, sales order number, or shipment date, helping in grouping shipment information.
It will also provide you with the shipment information like the carrier, the tracking number, the ship date, the ship to address, and the ship from address, that way we can provide the tracking information to your customers.
## Invoice Service(INVC)
This is a service so the suppliers can expose to you the invoices for the order. Main functions are *getInvoices* and *getVoidedInvoices*. You could look using *PONumber*, *Invoice Number*, *Date Search*, and *Available DateTime Search*.
---
## PPC 1.0.0
Source: https://docs.psrestful.com/standards/ppc-1.0.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

## Product Configuration, Decoration, and Pricing
### Version Details:
- **Version**: 1.0.0
- **Date**: 2017-07-19
### Document Change Log:
- **Initial Release**: 07/19/17
- **Design**: Eric Shonebarger (CIO Hit Promotional Products, Inc) and Eric Alessi (Essent Corporation).
### Contributors:
- **Design**:
- Eric Shonebarger
- Eric Alessi
- **Contributions**:
- Paul Fleischman (Technical Lead PCNA)
- Jon Norris (VP of Operations, Starline)
### Abstract:
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface. Additionally, this document will provide sample code in order to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
### Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at http://www.w3.org/TR/soap12-part1/
### Service Details:
- **Function [getAvailableLocations](#function-getavailablelocations)**: Provides names of locations for a given product.
- **Function [getDecorationColors](#function-getdecorationcolors)**: Describes possible decoration colors for a product.
### Additional Functions:
- **Function [getFobPoints](#function-getfobpoints)**: Returns information about FOB points for a product.
- **Function [getAvailableCharges](#function-getavailablecharges)**: Provides a list of charges and information on how to calculate them.
### Configuration and Pricing:
- **Function [getConfigurationAndPricing](#function-getconfigurationandpricing)**: Provides the pricing and configuration of a product.
---
## Function: getAvailableLocations()
This function provides the names of locations for a given product.
### Request: GetAvailableLocationsRequest
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| productId | The product id for which to get the locations | STRING | VARCHAR(64) | TRUE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country Example: CA=Canada; US=United States | STRING | VARCHAR(2) | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language. Example: en = English; fr = French | STRING | VARCHAR(2) | TRUE |
### Reply: GetAvailableLocationsResponse
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------------|-----------------------------------------------------------------------|----------------|---------------|-----------|
| [AvailableLocationArray](#availablelocation) | An array of locations | ARRAY | ARRAY | TRUE |
| [ErrorMessage](#errormessage) | List of possible values for errors. Values are enumerated—See Appendix A: Error Messages for full details. | STRING | VARCHAR(256) | FALSE |
## AvailableLocation
This array provides details about the available locations for a given product.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|--------------|---------------------------------|----------------|---------------|-----------|
| locationId | A unique Id of the location | INT | INT | TRUE |
| locationName | The name of the location | STRING | VARCHAR(64) | TRUE |
---
## Function: getDecorationColors()
This function describes possible decoration colors given a `productId`.
### Request: GetDecorationColorsRequest
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated: \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| locationId | The Id of the location | INT | INT | TRUE |
| productId | ProductId to filter down to a specific product. If left blank, the function will return all decoration colors for all products. | STRING | VARCHAR(64) | TRUE |
| decorationId | The Id of the decoration to filter requests by | INT | INT | FALSE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country Example: CA=Canada; US=United States | STRING | VARCHAR(2) | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language. Example: en = English; fr = French | STRING | VARCHAR(2) | TRUE |
### Reply: GetDecorationColorsResponse
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| [DecorationColors](#color) | The object with decoration colors | ARRAY | ARRAY | FALSE |
| [ErrorMessage](#errormessage) | List of possible values for errors. Values are enumerated—See Appendix A: Error Messages for full details. | | | FALSE |
###
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| [DecorationColors](#color) | The object with decoration colors | ARRAY | ARRAY | TRUE |
| productId | The Id of the product. | STRING | VARCHAR(64) | TRUE |
| locationId | The Id of the location that was provided | STRING | VARCHAR(64) | TRUE |
| [DecorationMethodArray](#decorationmethod)| An array of decoration methods for the location | ARRAY | ARRAY | TRUE |
| pmsMatch | TRUE, if PMS match is possible | BOOLEAN | BOOLEAN | TRUE |
| fullColor | Set to true if the decoration method is full color process; False implies that number of colors is irrelevant. | BOOLEAN | BOOLEAN | TRUE |
### Color
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------------|-------------------------------------------------|----------------|---------------|-----------|
| colorId | A unique Id of the color of the imprint | INT | INT | TRUE |
| colorName | The name of the imprint color | STRING | VARCHAR(64) | TRUE |
### DecorationMethod
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|-------------------------------------------------|----------------|---------------|-----------|
| decorationId | A unique Id of the decoration | INT | INT | TRUE |
| decorationName | The name of the decoration | STRING | VARCHAR(64) | TRUE |
---
## Function: getFobPoints()
This function will return basic information about FOB points for a given product.
### Request: GetFobPointsRequest
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated: \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| productId | The productId you are requesting FOB points for | STRING | VARCHAR(64) | TRUE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country Example: CA=Canada; US=United States | STRING | VARCHAR(2) | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language. Example: en = English; fr = French | STRING | VARCHAR(2) | TRUE |
### Reply: GetFobPointsResponse
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| [FobPointArray](#fobpoint) | Array of fob Points | ARRAY | ARRAY | FALSE |
| [ErrorMessage](#errormessage) | List of possible values for errors. Values are enumerated—See Appendix A: Error Messages for full details. | STRING | VARCHAR(256) | FALSE |
### FobPoint
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|--------------------------------------------------------------------|----------------|---------------|-----------|
| fobId | The Id of the FOB Point | STRING | VARCHAR(64) | TRUE |
| fobPostalCode | The Postal or Zip Code of the fob Point | STRING | VARCHAR(64) | TRUE |
| fobCity | The city of the FOB Point | STRING | VARCHAR(64) | TRUE |
| fobState | The state of the FOB Point in ISO 3166-2 format. | STRING | VARCHAR(64) | TRUE |
| fobCountry | The country of the FOB Point in Alpha 2 ISO3166 “CODE” format. | STRING | VARCHAR(64) | TRUE |
| [CurrencySupportedArray](#currencysupported) | The currencies supported for the FOB Point | ARRAY | ARRAY | TRUE |
| [ProductArray](#product) |An array of productIds associated with the FOB Point | ARRAY | ARRAY | TRUE |
### Product
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|--------------------------------------------------------------------|----------------|---------------|-----------|
| productId | The product id | STRING | VARCHAR(64) | TRUE |
### CurrencySupported
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|--------------------------------------------------------------------|----------------|---------------|-----------|
| currency | The currency supported for the FOB point in ISO4217 format. | STRING | VARCHAR(64) | TRUE |
---
## Function: getAvailableCharges()
This function provides a list of charges and information on how to calculate charges. It is designed to help populate a database of charge types and design logic to calculate charges.
### Request: GetAvailableChargesRequest
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated: \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| productId | The productId you are requesting charges for | STRING | VARCHAR(64) | FALSE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country Example: CA=Canada; US=United States | STRING | VARCHAR(2) | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language. Example: en = English; fr = French | STRING | VARCHAR(2) | TRUE |
### Reply: GetAvailableChargesResponse
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| [AvailableChargeArray](#availablecharge) | An array of charges | ARRAY | ARRAY | TRUE |
| [ErrorMessage](#errormessage) | List of possible values for errors. Values are enumerated—See Appendix A: Error Messages for full details. | STRING | VARCHAR(256) | FALSE |
### AvailableCharge
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|-------------------------------------------------------------------------------|----------------|---------------|-----------|
| chargeId | A unique Id of the charge | INT | INT | TRUE |
| chargeName | The name of the charge | STRING | VARCHAR(64) | TRUE |
| chargeType | The type of charge. Values are enumerated \{SETUP, RUN, ORDER\}. | STRING | VARCHAR(64) | TRUE |
| chargeDescription | The charge description | STRING | VARCHAR(256) | TRUE |
---
## Function: getConfigurationAndPricing()
This function is the main function of the service and provides the pricing and configuration of a product.
### Request: GetConfigurationAndPricingRequest
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|--------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated: \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| productId | The productId for the pricing | STRING | VARCHAR(64) | TRUE |
| partId | The partId | STRING | VARCHAR(64) | FALSE |
| currency | The unit of currency that the pricing should be returned in ISO4217 “CODE” format. | STRING | VARCHAR(64) | TRUE |
| fobId | The fobId of the FOB point | STRING | VARCHAR(64) | TRUE |
| priceType | The type of pricing that should be returned. Values are enumerated: \{Customer, List, Net \} | STRING | VARCHAR(64) | TRUE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country Example: CA=Canada; US=United States | STRING | VARCHAR(2) | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language. Example: en = English; fr = French | STRING | VARCHAR(2) | TRUE |
| configurationType | The type of configuration of the product to be returned.
Values are enumerated: \{Blank, Decorated\} | STRING | VARCHAR(32) | TRUE |
### Reply: GetConfigurationAndPricingResponse
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| [Configuration](#configuration) | An object to hold Configuration data. | OBJECT | OBJECT | TRUE |
| [ErrorMessage](#errormessage) | Response for any error requiring notification to requestor | OBJECT | OBJECT | FALSE |
### Configuration
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| [PartArray](#partarray) | An array of Parts and their pricing and configuration data | ARRAY | ARRAY | TRUE |
| [LocationArray](#locationarray) | An array of Locations and their pricing and configuration data | ARRAY | ARRAY | FALSE |
| productId | The product family. This is how the part is marketed. | STRING | VARCHAR(64) | TRUE |
| currency | The currency the request of the request in ISO 4217 format | STRING | VARCHAR(64) | TRUE |
| [FobArray](#fobarray) | An array of FOB points that support this configuration | ARRAY | ARRAY | TRUE |
| fobPostalCode | The postal code of the FOB point | STRING | VARCHAR(64) | FALSE |
## PartArray
This object contains information about a part. Parts can either be the main part, required parts, or optional parts that customers can use to configure a product. The `partId` and `partGroup` combination should be unique.
### Part
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| partId | The `partId` | STRING | VARCHAR(64) | TRUE |
| partDescription | A description of the `partId` | STRING | VARCHAR(128) | FALSE |
| [PartPriceArray](#partprice) | An array of prices and quantities | ARRAY | ARRAY | FALSE |
| partGroup | A numeric identifier grouping mutually exclusive parts together. | NUMBER | INT | TRUE |
| nextPartGroup | The next mutually exclusive `partGroup` to complete configuration of the product | NUMBER | INT | FALSE |
| partGroupRequired | A boolean value specifying if this `partGroup` is required for the product configuration | BOOLEAN | BOOLEAN | TRUE |
| partGroupDescription | A description of the `partGroup`. Examples: "Main Product", "Optional Lid", "Straw", etc. | STRING | VARCHAR(64) | TRUE |
| ratio | Describes how the amount of partIds that need to be added to the order based on the number of products ordered. Example: If 8 partIds would be required per 1 product ordered, then 8 should be used as the ratio. If one partId is required for every 8 products, then use .125 | DOUBLE | DECIMAL (12,4) | TRUE |
| defaultPart | This part is included in the “Basic Pricing Configuration” service price. This field is optional, but highly encouraged. | BOOLEAN | BOOLEAN | FALSE |
| [LocationIdArray](#locationidarray) | An array of LocationIDs that are available for decoration because the selected part has been configured. | ARRAY | ARRAY | FALSE |
### PartPrice
This object contains pricing for a product based on the `partId`. Prices are additive in nature. A configuration that requires three parts with three different prices should be summed together on the purchase order.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|--------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| minQuantity | The minimum quantity for the price break. | INT | INT | TRUE |
| price | The base price of the good without decoration | DOUBLE | DECIMAL (12,4)| TRUE |
| discountCode | The industry discount code associated with the price. | STRING | VARCHAR(1) | FALSE |
| priceUom | Enumerated list of unit of measure used to describe the price. Values are: \{BX, CA, DZ, EA, KT, PR, PK, RL, ST, SL, TH\}. BX - Box, CA - Case, DZ - Dozen, EA - Each, KT - Kit, PR - Pair, PK - Package, RL - Roll, ST - Set, SL - Sleeve, TH - Thousand | STRING | VARCHAR(2) | TRUE |
| priceEffectiveDate | The date the price is effective in ISO8601 format. | DATE | DATE | TRUE |
| priceExpiryDate | The date the price is no longer effective in ISO8601 format. | DATE | DATE | TRUE |
### LocationIdArray
This is a list of locationIds that are valid based on the partID that was configured.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------------|--------------------------------------------------|----------------|---------------|-----------|
| locationID | The Id of the available location. | INT | INT | TRUE |
## LocationArray
This object contains information about available locations for a given product.
### Location
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|------------------------------------------------------------------------------|----------------|---------------|-----------|
| locationId | A unique Id of the location | INT | INT | TRUE |
| locationName | The name of the location | STRING | VARCHAR(64) | TRUE |
| [DecorationArray](#decorationarray) | An array of decorations that are available for the location. | ARRAY | ARRAY | TRUE |
| decorationsIncluded | The number of decorations included in the price | INT | INT | TRUE |
| maxDecoration | The maximum number of decorations that can be added to the location | INT | INT | TRUE |
| minDecoration | The minimum number of decorations that can be added to the location | INT | INT | TRUE |
| locationRank | Popularity of location based on supplier experience | INT | INT | FALSE |
---
### DecorationArray
This array provides details about the decorations that are valid for a given location.
### Decoration
This object contains decoration information that is valid for a specific location.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| decorationId | The ID of the decoration | INT | INT | TRUE |
| decorationName | The name of the decoration | STRING | VARCHAR(64) | FALSE |
| decorationGeometry | The geometry of the decoration. Values are enumerated: \{Circle, Rectangle, Other\} | STRING | VARCHAR(64) | TRUE |
| decorationHeight | The maximum imprint height of the decoration; leave blank if the imprint is not rectangular | DOUBLE | DECIMAL (12,4) | FALSE |
| decorationWidth | The maximum imprint width of the decoration; leave blank if the imprint is not rectangular | DOUBLE | DECIMAL (12,4) | FALSE |
| decorationDiameter | The maximum imprint diameter of the decoration; leave blank if the imprint is not circular | DOUBLE | DECIMAL (12,4) | FALSE |
| decorationUom | The unit of measure for the decoration area in ISO 20022 format | STRING | VARCHAR(64) | TRUE |
| allowSubForDefaultLocation | Buyer is allowed to substitute a decoration location without changing the price | BOOLEAN | BOOLEAN | FALSE |
| allowSubForDefaultMethod | Buyer is allowed to substitute this decoration method without changing the price | BOOLEAN | BOOLEAN | FALSE |
| itemPartQuantityLTM | Specifies the Part Quantity that is the absolute minimum that can be ordered with a Less Than Minimum (LTM) charge | INT | INT | FALSE |
| [ChargeArray](#charge) | An array of setup charge data | ARRAY | ARRAY | FALSE |
| decorationUnitsIncluded | The number of included decoration units. For example, if 1 color decoration is included set value to “1”. If 7,500 stitches are included set value to “7500” | INT | INT | FALSE |
| decorationUnitsIncludedUom | Values are enumerated: \{Colors, Inches, Other, Stitches, SquareInches \} | STRING | STRING | FALSE |
| decorationUnitsMax | This is the max number of decoration units for this decoration/location combination | INT | INT | FALSE |
| defaultDecoration | Specifies whether this is the default decoration for this location | BOOLEAN | BOOLEAN | TRUE |
| leadTime | The lead time for the given decoration | INT | INT | FALSE |
| rushLeadTime | The lead time for rush service for a given decoration (rush charges may apply) | INT | INT | FALSE |
## Charge
This object contains a charge that is associated with the setup of the product, location, and decoration configuration.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|-------------------------------------------------------------------------------|----------------|---------------|-----------|
| chargeId | The Id of the charge | INT | INT | TRUE |
| chargeName | The name of the charge | STRING | VARCHAR(64) | TRUE |
| chargeType | The type of charge. Values are enumerated `{Order, Run, Setup`}. | STRING | VARCHAR(64) | TRUE |
| chargeDescription | The charge description | STRING | VARCHAR(256) | TRUE |
| [ChargePriceArray](#chargeprice) | An array of charge prices | ARRAY | ARRAY | TRUE |
| chargesAppliesLTM | This charge is applied with ordering Less than Minimum (LTM). | BOOLEAN | BOOLEAN | TRUE |
| chargesPerLocation | The number of times a charge will occur per location | INT | INT | FALSE |
| chargesPerColor | The number of times a charge will occur per color | INT | INT | FALSE |
## ChargePrice
This object contains a single line of a price grid represented by an X and Y axis.
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|-----------------------------------------------------------------------------|----------------|---------------|-----------|
| xMinQty | The minimum x-value quantity for this price | INT | INT | TRUE |
| xUom | The unit of measure for the x-axis. Values are enumerated: \{BX, CA, DZ, EA, KT, PR, PK, RL, ST, SL, TH\} | STRING | VARCHAR(2) | TRUE |
| yMinQty | The minimum y-value quantity for this price | INT | INT | TRUE |
| yUom | The unit of measure for the y-axis. Values are enumerated: \{Colors, Inches, Other, Stitches, SquareInches\} | STRING | STRING | TRUE |
| price | The price of the charge | DOUBLE | DECIMAL (12,4)| TRUE |
| discountCode | The discount code associated with the price | STRING | VARCHAR(1) | FALSE |
| repeatPrice | The price of the charge if it is a repeat order | NUMBER | DECIMAL (12,4)| FALSE |
| repeatDiscountCode | The discount code of the repeat price | STRING | VARCHAR(1) | FALSE |
| priceEffectiveDate | The date the price is effective in ISO8601 format | DATE | DATE | FALSE |
| priceExpiryDate | The date the price is no longer effective in ISO8601 format | DATE | DATE | FALSE |
## FobArray
This object contains information about the FOB (Free On Board) points that support the configuration. By default, this will always include the valid FOB point that was sent as part of the request.
### FOB Point
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|--------------------------------------------------|----------------|---------------|-----------|
| fobId | The FOB point of the pricing | STRING ARRAY | VARCHAR(64) | TRUE |
| fobPostalCode | The postal code of the FOB point | STRING | VARCHAR(64) | FALSE |
---
## Appendix A: Error Messages
These error messages are associated with the setup of the product, location, and decoration configuration.
### ErrorMessage
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------|-----------------------------------------------------|----------------|---------------|-----------|
| code | The numerical value of the code | INT | INT | TRUE |
| description | Response for any error requiring notification to requestor | STRING | VARCHAR(256) | FALSE |
### Standardized Codes
The range of `100-199` has been reserved for standardized error codes. The number `999` has been reserved for an error code that is a "General Error - Contact System Service Provider".
| Code | Description |
|------|-------------|
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported:
| 999 | General Error – Contact the System Service Provider Details: [Details] |
### Service Specific Code
These error codes are only for this service.
| Code | Description |
|------|-------------|
| 400 | productID not found |
| 401 | currencyID not found |
| 402 | priceType not found |
| 403 | fobId not found |
| 404 | localizationCountry not found |
| 405 | localizationLanguage not found |
| 406 | configurationType not found |
## Diagrams
### getAvailableLocations Function
```mermaid
classDiagram
class GetAvailableLocationsRequest {
-wsVersion: String
-id: String
-password: String?
-productId: String
-localizationCountry: String
-localizationLanguage: String
}
class GetAvailableLocationsResponse {
-AvailableLocationArray: AvailableLocation[]
-ErrorMessage: ErrorMessage?
}
class AvailableLocation {
-locationId: Int
-locationName: String
}
class ErrorMessage {
-code: Number
-description: String
}
GetAvailableLocationsRequest --> GetAvailableLocationsResponse
GetAvailableLocationsResponse --> AvailableLocation
```
### getDecorationColors Function
```mermaid
classDiagram
class GetDecorationColorsRequest {
-wsVersion: String
-id: String
-password: String?
-productId: String
-locationId: Int?
-decorationId: Int?
-localizationCountry: String
-localizationLanguage: String
}
class GetDecorationColorsResponse {
-DecorationColors: DecorationColors
-ErrorMessage: ErrorMessage?
}
class DecorationColors {
-ColorArray: Color[]
-productId: String
-locationId: String
-DecorationMethodArray: DecorationMethod[]
-pmsMatch: Boolean
-fullColor: Boolean
}
class Color {
-colorId: Int
-colorName: String
}
class DecorationMethod {
-decorationId: Int
-decorationName: String
}
GetDecorationColorsRequest --> GetDecorationColorsResponse
GetDecorationColorsResponse --> DecorationColors
DecorationColors --> Color
DecorationColors --> DecorationMethod
```
### getFobPoints Function
```mermaid
classDiagram
class GetFobPointsRequest {
-wsVersion: String
-id: String
-password: String?
-productId: String?
-localizationCountry: String
-localizationLanguage: String
}
class GetFobPointsResponse {
-FobPointArray: FobPoint[]
-ErrorMessage: ErrorMessage?
}
class FobPoint {
-fobId: String
-fobPostalCode: String
-fobCity: String
-fobState: String
-fobCountry: String
-CurrencySupportedArray: String[]
-ProductArray: String[]
}
GetFobPointsRequest --> GetFobPointsResponse
GetFobPointsResponse --> FobPoint
```
### getAvailableCharges Function
```mermaid
classDiagram
class GetAvailableChargesRequest {
-wsVersion: String
-id: String
-password: String?
-productId: String?
-localizationCountry: String
-localizationLanguage: String
}
class GetAvailableChargesResponse {
-AvailableChargeArray: AvailableCharge[]
-ErrorMessage: ErrorMessage?
}
class AvailableCharge {
-chargeId: Int
-chargeName: String
-chargeType: String
-chargeDescription: String
}
GetAvailableChargesRequest --> GetAvailableChargesResponse
GetAvailableChargesResponse --> AvailableCharge
```
### GetConfigurationAndPricing Function
```mermaid
classDiagram
class GetConfigurationAndPricingRequest {
-wsVersion: String
-id: String
-password: String?
-productId: String
-partId: String?
-currency: String
-fobId: String
-priceType: String
-localizationCountry: String
-localizationLanguage: String
-configurationType: String
}
class GetConfigurationAndPricingResponse {
-Configuration: Configuration
-ErrorMessage: ErrorMessage?
}
class Configuration {
-PartArray: Part[]
-LocationArray: Location[]
-productId: String
-currency: String
-FobArray: Fob[]
-fobPostalCode: String
-priceType: String
}
class Part {
-partId: String
-partDescription: String
-PartPriceArray: PartPrice[]
-partGroup: Int
-nextPartGroup: Int
-partGroupRequired: Boolean
-partGroupDescription: String
-ratio: Double
-defaultPart: Boolean
-LocationIdArray: Int[]
}
class PartPrice {
-minQuantity: Int
-price: Double
-discountCode: String
-priceUom: String
-priceEffectiveDate: Date
-priceExpiryDate: Date
}
class Location {
-locationId: Int
-locationName: String
-DecorationArray: Decoration[]
-decorationsIncluded: Int
-defaultLocation: Boolean
-maxDecoration: Int
-minDecoration: Int
-locationRank: Int
}
class Decoration {
-decorationId: Int
-decorationName: String
-decorationGeometry: String
-decorationHeight: Double
-decorationWidth: Double
-decorationDiameter: Double
-decorationUom: String
-allowSubForDefaultLocation: Boolean
-allowSubForDefaultMethod: Boolean
-itemPartQuantityLTM: Int
-ChargeArray: Charge[]
-decorationUnitsIncluded: Int
-decorationUnitsIncludedUom: String
-decorationUnitsMax: Int
-defaultDecoration: Boolean
-leadTime: Int
-rushLeadTime: Int
}
class Charge {
-chargeId: Int
-chargeName: String
-chargeType: String
-chargeDescription: String
-ChargePriceArray: ChargePrice[]
-chargeAppliesLTM: Boolean
-chargesPerLocation: Int
-chargesPerColor: Int
}
class ChargePrice {
-xMinQty: Int
-xUom: String
-yMinQty: Int
-yUom: String
-price: Double
-discountCode: String
-repeatPrice: Number
-repeatDiscountCode: String
-priceEffectiveDate: Date
-priceExpiryDate: Date
}
GetConfigurationAndPricingRequest --> GetConfigurationAndPricingResponse
GetConfigurationAndPricingResponse --> Configuration
Configuration --> Part
Part --> PartPrice
Configuration --> Location
Location --> Decoration
Decoration --> Charge
Charge --> ChargePrice
```
---
---
## Product Compliance 1.0.0
Source: https://docs.psrestful.com/standards/product-compliance-1.0.0
# Product Compliance 1.0.0
**PROMOTIONAL PRODUCTS COMPLIANCE INTERFACE SPECIFICATION FOR WEB SERVICES**

Version: 1.0.0
Date: 2020-01-08
## Document Change Log
| Version | Date | Reason for Change | Author |
| ------- | ---------- | ------------------ | ------------------------------------------------------------------ |
| 1.0.0 | 01/08/2020 | Production Release | Dustin Kessler, Enterprise Architect, Staples Promotional Products |
## Contributors
The following have contributed to the creation of this specification:
Contributors: Dustin Kessler, Enterprise Architect, Staples Promotional Products
Max Stepanskiy, Executive Director of Enterprise Architecture, Advertising Specialty Institute
Eric Shonebarger, CIO, Hit Promotional Products, Inc
Michael J Plourde, Director Data and Analytics, Geiger
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required to build the interface. Additionally, this document will provide sample code to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standard based on a secure form of communication.
More information on SOAP can be found at http://www.w3.org/TR/soap12-part1/
### **Service Details**
### **Function** : getProductsWithRegulations ()
Gets the list of products and optional parts which currently or in the future will have applicable regulations
### **Request:** GetProductsWithRegulationsRequest
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced. _Values are enumerated \{1.0.0\}_ | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
| changeTimeStamp | Beginning date time since last change in UTC; this element being left off the request indicates an intention to get all whereas including this element in the request indicates an intention to only get those that have changed since the supplied date and time. | ISO 8601 DATE | DATETIME | FALSE |
### **Reply:** GetProductsWithRegulationsResponse
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| [productWithRegulationsArray](#productwithregulations-object) | The list of products and optional their parts which currently or in the future will have applicable regulations | OBJECT ARRAY | OBJECT ARRAY | TRUE |
| [ServiceMessageArray](#servicemessage) | Error message object Array | OBJECT ARRAY | OBJECT ARRAY | FALSE |
### ProductWithRegulations Object
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| productId | The identifier of the product that has or will have applicable regulations | STRING | VARCHAR(64) | TRUE |
| partId | The identifier of the part that has or will have applicable regulations; this element being left off the response indicates all parts have or will have applicable regulations whereas including this element in the response indicates this part has or will have applicable regulations | STRING | VARCHAR(64) | FALSE |
| complianceGroups | The list of promo standards compliance groups which currently or in the future apply to this product and, if included in the response, part Values are enumerated \{CaliforniaProp65\} | ENUMERATED STRING ARRAY | VARCHAR(64) ARRAY | TRUE |
### **Function** : getCompliance ()
Gets the compliance data of the specific product and part, if supplied, for the `CaliforniaProp65` compliance group.
### **Request:** GetComplianceRequest
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customer Id or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the Id. | STRING | VARCHAR(64) | FALSE |
| productId | The identifier of the product to get compliance data for | STRING | VARCHAR(64) | TRUE |
| partId | The identifier of the part to get compliance data for; this element being left off indicates an intention to get compliance data on any applicable parts for the specific product whereas including this element indicates an intention to only get compliance data for the specific part | STRING | VARCHAR(64) | FALSE |
### **Reply:** GetComplianceResponse
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| [ComplianceData](#compliancedata) | An object containing the compliance data for a product; this element being left off indicates no compliance data is available for the requested product or, if requested, part. | OBJECT | OBJECT | FALSE |
| [ServiceMessage](#servicemessage) | Service message object | OBJECT | OBJECT | FALSE |
### **ComplianceData**
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| productId | The identifier of the product associated with the compliance data | STRING | VARCHAR(64) | TRUE |
| partId | The identifier of the part associated with the compliance data; this element being left off indicates the compliance data applies to all parts whereas including this element in the response indicates the compliance data is for the part | STRING | VARCHAR(64) | FALSE |
| [CAProp65](#caprop65) | An object containing the `CaliforniaProp65` compliance data for a product; this element being left off indicates no compliance data is available for the requested product or, if requested, part. | OBJECT | OBJECT | FALSE |
### **CAProp65**
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| ApplicableWarnings | The warning(s) which apply to the product, or if specified in the response, part | OBJECT ARRAY | OBJECT ARRAY | TRUE |
| policyForApplyingLabel | The trigger for applying the warning label to the product or product packaging Values are enumerated \{Applied Upon Request, Applied When Shipping to California, Applied Always\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
### **CAProp65Warning**
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| applicableWarning | A warning that applies to the product, or if specified in the response, part Values are enumerated \{On Product Cancer, On Product Reproductive, Food, Furniture, Alcoholic Beverage\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
| applicableChemicals | The chemicals used on or in the product, or if specified in the response, part, that cause the warning to apply to the product or part | STRING ARRAY | STRING ARRAY | FALSE |
| warningLabelApplied | The type of warning label applied to the product or product packaging Values are enumerated \{Short Form, Long Form\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
## Appendix A: Error Messages
### ServiceMessage
| **Field** | **Description** | **WSDL Data Type** | **SQL Data Type** | **Required?** |
| --- | --- | --- | --- | --- |
| code | The numerical value of the code | INT | INT | TRUE |
| description | Response for any message requiring notification to requestor | STRING | VARCHAR(256) | TRUE |
| severity | The severity of the message. Values are enumerated: \{Error, Information, Warning\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
### Standardized Codes:
The range of 100-199 has been reserved for standardized error codes.
The number 999 has been reserved for an error codes that is a "General Error - Contact System Service Provider"
| **Code** | **Description** |
| --- | --- |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 999 | General Error – Contact the System Service Provider Details: [Details] |
**Service Specific Code:** These error codes are only for this service.
| Code | Description |
| --- | --- |
| 600 | Product Id not found |
| 610 | Data violation: \{0\}
(Occurs when a supplier does a pre-check on the data and finds that there is a mismatch of data and/or there are incorrect calculations) |
| 620 | Field is not supported: \{0\} |
| 630 | Part Id not found |
## Diagrams
### GetProductsWithRegulations Request and Response Classes
```mermaid
classDiagram
class GetProductsWithRegulationsRequest {
+String wsVersion
+String id
+String password
+ISO 8601 changeTimeStamp
}
class GetProductsWithRegulationsResponse {
+ProductWithRegulationsArray productWithRegulationsArray
+ServiceMessageArray serviceMessageArray
}
class ProductWithRegulations {
+String productId
+String partId
+ComplianceGroupArray complianceGroups
}
class ComplianceGroup {
+String complianceGroupName
}
class ServiceMessage {
+Int code
+String description
+String severity
}
GetProductsWithRegulationsRequest --> GetProductsWithRegulationsResponse : returns
GetProductsWithRegulationsResponse --> ProductWithRegulations : contains
ProductWithRegulations --> ComplianceGroup : contains
GetProductsWithRegulationsResponse --> ServiceMessage : contains
```
### GetCompliance Request and Response Classes
```mermaid
classDiagram
class GetComplianceRequest {
+String wsVersion
+String id
+String password
+String productId
+String partId
}
class GetComplianceResponse {
+ComplianceData complianceData
+ServiceMessage serviceMessage
}
class ComplianceData {
+String productId
+String partId
+CAProp65 caProp65
}
class CAProp65 {
+CAProp65WarningArray applicableWarnings
+String policyForApplyingLabel
}
class CAProp65Warning {
+String applicableWarning
+StringArray applicableChemicals
+String warningLabelApplied
}
GetComplianceRequest --> GetComplianceResponse : returns
GetComplianceResponse --> ComplianceData : contains
ComplianceData --> CAProp65 : contains
CAProp65 --> CAProp65Warning : contains
GetComplianceResponse --> ServiceMessage : contains
```
---
## Product Data 1.0.0
Source: https://docs.psrestful.com/standards/product-data-1.0.0
### **PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES**
## **Product Data 1.0.0**

Version: 1.0.0
Date: 2016-06-08
**Document Change Log**
| Version | Date | Reason for Change | Author |
| --- | --- | --- | --- |
| 1.0.0 | 2016-06-08 | Initial Release | Tim Nale, Director IT BIC Graphic NA |
**Contributors**
The following have contributed to the creation of this specification:
**Design** : Tim Nale, Director of IT, BIC Graphic
**Contributors** :
- Paul Fleischman, Technical Lead, PCNA
- Jon Norris, VP of Operations, Starline
**Product Data**
There will be four functions available as part of this web service.
- [getProduct()](#getproduct)
- [getProductDateModified()](#getproductdatemodified)
- [getProductCloseOut()](#getproductcloseout)
- [getProductSellable()](#getproductsellable)
### getProduct()
This function will provide the detail product data based on a specific ProductId or any additional optional subset filtering provided at the request.
### **Request:** GetProductRequest
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country
Example:
CA=Canada
US=United States | 2 STRING | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language
Example:
en = English
fr = French | 2 STRING | TRUE |
| productId | Item's ID | 64 STRING | TRUE |
| partId | The Supplier specific PartId; commonly referred to as a sku | 64 STRING | FALSE |
| colorName | A specific part color for this product | 64 STRING | FALSE |
| [ApparelSizeArray](#apparelsize-object) | Used for returning specific apparel sizes. Array of ApparelSize objects | ARRAY | FALSE |
### **Reply:** GetProductResponse
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| [Product](#product-object) | Product Information | OBJECT | FALSE |
| [ErrorMessage](#errormessage-object) | Response for any error requiring notification to requestor | OBJECT | FALSE |
### **Product Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| productId | The associated product. | 64 STRING | TRUE |
| productName | The Supplier name for the product | 256 STRING | TRUE |
| description | Basic product description or bulleted list of descriptions. | 2048 STRING ARRAY | TRUE |
| [ProductMarketingPointArray](#productmarketingpoint-object) | Marketing points type and content. An array of MarketingPoint objects. | OBJECT ARRAY | FALSE |
| [ProductKeywordArray](#productkeyword-object) | Array of keywords often used in searching for this product. An array of ProductKeyword objects. | OBJECT ARRAY | FALSE |
| productBrand | The specific product brand | 64 STRING | FALSE |
| export | Product status for export | NILLABLE BOOLEAN | TRUE |
| [ProductCategoryArray](#productcategory-object) | The product's categorization array. An array of ProductCategory objects. | OBJECT ARRAY | FALSE |
| [RelatedProductArray](#relatedproduct-object) | Products related to the quested product as Substitutable, CompanionSell, or Common Grouping (Family). An array of RelatedProduct objects. | OBJECT ARRAY | FALSE |
| [ProductPartArray](#productpart-object) | All part specific product data. An array of ProductPart objects. | OBJECT ARRAY | TRUE |
| lastChangeDate | The date time stamp of the most recent change to this data in ISO 8601 format | ISO 8601 DATE | TRUE |
| creationDate | The date time stamp when this products data initially became available in ISO 8601 format | ISO 8601 DATE | TRUE |
| endDate | The Date this Product expires from Supplier availability in ISO 8601 format | ISO 8601 DATE | FALSE |
| effectiveDate | The Date this Product initially becomes available from the Supplier in ISO 8601 format | ISO 8601 DATE | FALSE |
| isCaution | Cautionary status to review for specific warnings about using product data | NILLABLE BOOLEAN | TRUE |
| cautionComment | Product cautionary comments | 1024 STRING | FALSE |
| isCloseout | Indicates if the product is a closeout | NILLABLE BOOLEAN | TRUE |
| lineName | Line Name / Division to which this product belongs | 64 STRING | FALSE |
### **ProductCategory Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| category | Product category | 256 STRING | TRUE |
| subCategory | Product subcategory | 256 STRING | FALSE |
### **ProductMarketingPoint Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| pointType | Basic category or type of marketing point being made. e.g. Highlights, Size, Safety | 64 STRING | FALSE |
| pointCopy | Marketing bullet or point copy | 1024 STRING | TRUE |
### **ProductKeyword Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| keyword | A product keyword commonly utilized for search or other functions | 64 STRING | TRUE |
### **RelatedProduct Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| relationType | The relationship type between two products | 64 STRING | TRUE |
| productId | Product Id | 64 STRING | TRUE |
| partId | Part specific Id | 64 STRING | FALSE |
### **ProductPart Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| partId | The part Id | 64 STRING | TRUE |
| description | Description | 2048 STRING ARRAY | FALSE |
| countryOfOrigin | Country of Origin
ISO 3166-1 Alpha 2 code for Country
Example: CA=Canada;
US=United States | 2 STRING | FALSE |
| [ColorArray](#color-object) | Information about the part colors | OBJECT ARRAY | FALSE |
| primaryMaterial | Primary material of construction | 64 STRING | FALSE |
| [SpecificationArray](#specification-object) | Specifications | OBJECT ARRAY | FALSE |
| shape | General shape | 64 STRING | FALSE |
| [ApparelSize](#apparelsize-object) | Used to describe an apparel size | OBJECT | FALSE |
| [Dimension](#dimension-object) | Physical dimensions and weight | OBJECT | FALSE |
| leadTime | Lead time in days | INT | FALSE |
| unspsc | United Nations Standard Products and Services Code (UNSPSC) | 8 STRING | FALSE |
| gtin | Global Trade Item Number (GTIN) | 14 STRING | FALSE |
| isRushService | Rush service | NILLABLE BOOLEAN | TRUE |
| [ProductPackagingArray](#productpackaging-object) | Packaging option details; e.g. Gift Box, Cello Pack, Sleeve. An array of ProductPackaging objects. | OBJECT ARRAY | FALSE |
| [ShippingPackageArray](#shippingpackage-object) | Shipping package option details for the partPackaging Default package type; e.g. Carton, Box, Pallet. An array of ShippingPackage objects. | OBJECT ARRAY | FALSE |
| endDate | The date this part expires from supplier availability in ISO 8601 format | ISO 8601 DATE | FALSE |
| effectiveDate | The Date this Part initially becomes available from the Supplier in ISO 8601 format | ISO 8601 DATE | FALSE |
| isCloseout | Indicates if a closeout | NILLABLE BOOLEAN | TRUE |
| isCaution | Cautionary status to review for specific warnings about using product data | NILLABLE BOOLEAN | TRUE |
| cautionComment | Cautionary comments | 1024 STRING | FALSE |
| nmfcCode | National Motor Freight Classification Code | DECIMAL | FALSE |
| nmfcDescription | National Motor Freight Classification Description | 1024 STRING | FALSE |
| nmfcNumber | National Motor Freight Classification Number | 64 STRING | FALSE |
| isOnDemand | Manufactured on demand / Made to order | NILLABLE BOOLEAN | TRUE |
| isHazmat | Contains hazardous material. A nil value indicates this it is unknown or the data is not available by the supplier. | NILLABLE BOOLEAN | TRUE |
### **Color Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| -------------- | ------------------------------------------------------------------------ | ------------- | ------------- |
| colorName | Supplier specific color name. Go to market color | 64 STRING | TRUE |
| hex | The HEX code representation. This is not a supplement for product color but data used for generating web user interfaces. | 64 STRING | FALSE |
| approximatePms | The PMS code representation. This is not a supplement for product color. | 64 STRING | FALSE |
### **Specification Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| specificationType | The type of specification | 64 STRING FACIT | TRUE |
| SpecificationUom | Specification unit of measure | 64 STRING | TRUE |
| measurementValue | Specification Measurement Value | 64 STRING | TRUE |
### **ApparelSize Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| ------------ | --------------- | --------------- | ------------- |
| apparelStyle | Apparel style | 64 STRING FACIT | TRUE |
| labelSize | The apparel items tagged size. e.g. XS, S, M, L, etc. | 6 STRING FACIT | TRUE |
| customSize | Used to communicate custom size when labelSize is CUSTOM. | 64 STRING | FALSE |
### **Dimension Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| ------------ | --------------- | ------------- | ------------- |
| dimensionUom | The dimensional unit of measure | 2 STRING FACIT | TRUE |
| depth | The depth/length of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| height | The height of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| width | The width of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| weightUom | The weight unit of measure for the part | 2 STRING FACIT | TRUE |
| weight | The weight of the part using the unit of measure specified in weightUom | DECIMAL | FALSE |
### **ProductPackaging Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| default | Boolean flag identifying the single default packaging for this package | BOOLEAN | TRUE |
| packageType | The packaging specific type for this partId e.g. Gift Box, Cello Pack, Sleeve | 256 STRING | TRUE |
| description | The Packaging specific description | 2048 STRING | FALSE |
| quantity | The partId unit Quantity included within this specific Packaging Option | DECIMAL | TRUE |
| dimensionUom | The unit of measure for the package | 2 STRING FACIT | TRUE |
| depth | The depth/length of the package in the dimensionUom unit of measure | DECIMAL | FALSE |
| height | The height of the package in the dimensionUom unit of measure | DECIMAL | FALSE |
| width | The width of the package in the dimensionUom unit of measure | DECIMAL | FALSE |
| weightUom | The weight unit of measure for the packageType | 2 STRING FACIT | TRUE |
| weight | The weight of the specific packageType | DECIMAL | FALSE |
### **ShippingPackage Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| ------------ | --------------- | ------------- | ------------- |
| packageType | The shipping package specific type for this shipping package e.g. Box, Carton, etc. | 256 STRING | TRUE |
| description | The shipping package specific description | 2048 STRING | FALSE |
| quantity | The partId unit quantity included within this specific shipping packaging option | DECIMAL | TRUE |
| dimensionUom | The weight unit of measure for the packageType | 2 STRING FACIT | TRUE |
| depth | The depth/length of the shipping package in the dimensionUom unit of measure | DECIMAL | FALSE |
| height | The height of the shipping package in the dimensionUom unit of measure | DECIMAL | FALSE |
| width | The width of the shipping package in the dimensionUom unit of measure | DECIMAL | FALSE |
| weightUom | The weight unit of measure for the packageType | 2 FACIT STRING | TRUE |
| weight | The weight of the specific packageType | DECIMAL | FALSE |
### getProductDateModified()
This function will provide the list of Product Ids and optional Part Ids for all items which have changed since the date supplied in the request.
**Request:** GetProductDateModifiedRequest
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
| changeTimeStamp | Beginning date time since last change in UTC | ISO 8601 DATE | TRUE |
### **Reply:** GetProductDateModifiedResponse
| **Field** | **Description** | **Data Type** | **Required?** |
| ------------------ | --------------- | --- | --- |
| [productChangeArray](#productchange-object) | Array listing of all Product and Part specific Ids which have changed since the reference date requested (lastChangeDate). An array of ProductChange objects. | OBJECT ARRAY | TRUE |
| [ErrorMessage](#errormessage-object) | Response for any error requiring notification to requestor | OBJECT | FALSE |
### **ProductChange Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --------- | --------------- | --- | --- |
| productId | The associated product | 64 STRING | TRUE |
| partId | The associated part | 64 STRING | FALSE |
### getProductCloseOut()
This function will provide a list of Product Ids and optional Part Ids for all items which currently have an isCloseOut value of TRUE.
### **Request:** GetProductCloseOutRequest
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
### **Reply:** GetProductCloseOutResponse
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| [ProductCloseOutArray](#productcloseout-object) | All product and part specific Ids which are on closeout. An array of ProductCloseOut objects. | OBJECT ARRAY | TRUE |
| [ErrorMessage](#errormessage-object) | Response for any error requiring notification to requestor | OBJECT | FALSE |
**ProductCloseOut Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| productId | The associated product | 64 STRING | TRUE |
| partId | The associated part | 64 STRING | FALSE |
### getProductSellable()
This function will provide a list of Product Ids and optional Part Ids along with their status of available to sell (Sellable TRUE or FALSE).
### **Request:** GetProductSellableRequest
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
| productId | The associated product | 64 STRING | FALSE |
| partId | The associated part | 64 STRING | FALSE |
| isSellable | Indicates if sellable | BOOLEAN | TRUE |
### **Reply:** GetProductSellableResponse
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| [ProductSellableArray](#productsellable-object) | Array listing of all product and part specific Ids along with sellable status. An array of ProductSellable objects. | OBJECT ARRAY | TRUE |
| [ErrorMessage](#errormessage-object) | Response for any error requiring notification to requestor | OBJECT | FALSE |
### **ProductSellable Object**
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| productId | The associated product | 64 STRING | TRUE |
| partId | The associated part | 64 STRING | FALSE |
**Enumerated Type Restrictions**
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING FACET | 1.0.0 |
| --- | --- | --- | --- |
| dimensionUom | The dimensional unit of measure | 64 STRING FACET | MM (Millimeter)
CM (Centimeter)
MR (Meter)
IN (Inch)
FT (Feet)
YD (Yards) |
| weightUom | The weight unit of measure | 2 STRING FACET | ME (Milligram)
KG (Kilogram)
OZ (Ounce)
LB (Pound) |
| relationType | The relationship type between the two products | 64 STRING FACET | Substitute
Companion Sell
Common Grouping |
| specificationType | List of possible values. The type of specification. | 64 STRING FACET | Length
Thickness
Radius
Volume
Capacity
Memory
Data Ports
Capacitance
Voltage
Point Size
Sheet Size
Sheet Count
Pockets
Inseam
Bust
Chest
Waist
Hips
Cup
Rise
Neck
Thigh
Shoulders
Sleeve
Device Size |
| apparelStyle | List of possible values. e.g. Ladies, Men, Youth, etc. | 64 STRING FACET | Unisex
Youth
Girls
Boys
Womens
WomensTall
Mens
MensTall |
| labelSize | List of possible values. The apparel items tagged size. e.g. XS, S, M, XL, etc. | 4 STRING FACET | OSFA (One Size Fits All)
4XS (4 Extra Small)
3XS (3 Extra Small)
2XS (2 Extra Small)
XS (Extra Small)
S
M
L
XL (Extra Large)
2XL (2 Extra Large)
3XL (3 Extra Large)
4XL (4 Extra Large)
5XL (5 Extra Large)
CUSTOM |
### ErrorMessage Object
| **Field** | **Description** | **Data Type** | **Required?** |
| --- | --- | --- | --- |
| code | The numerical value of the code | NUMBER | YES |
| description | Response for any error requiring notification to requestor | 256 STRING | YES |
**Standardized Codes:** The range of 100-199 has been reserved for standardized error codes. The number 999 has been reserved for an error codes that is a "General Error - Contact System Service Provider"
| **Code** | **Description** |
| -------- | --- |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 130 | Product Id not found |
| 135 | Product color not found |
| 140 | Part Id not found |
| 145 | Part color not found |
| 150 | Part size not found |
| 999 | General Error – Contact the System Service Provider |
### **Code Samples**
To be published under separate specification: " "
**DATA MINING / ABUSE GUIDELINE**
The use of services detailed within this specification are to be conducted within the current guidelines of the "Interface Data Use Guidelines Standards"
**Validation of Services**
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by using the web service validation tool.
[https://services.promostandards.org/webserviceValidator/home](https://services.promostandards.org/webserviceValidator/home)
Select the service, version, method and input your endpoint. If the endpoint is correct you should receive a message of:
"The XML response is valid."
### Diagrams
```mermaid
classDiagram
class GetProductRequest {
-wsVersion: String
-id: String
-password: String
-localizationCountry: String
-localizationLanguage: String
-productId: String
-partId: String
-colorName: String
-ApparelSizeArray: ApparelSize[]
}
class ApparelSize {
-apparelStyle: String
-labelSize: String
-customSize: String
}
class GetProductResponse {
-Product: Product
-ErrorMessage: ErrorMessage
}
class Product {
-productId: String
-productName: String
-description: String[]
-priceExpiresDate: Date
-ProductMarketingPointArray: MarketingPoint[]
-ProductKeywordArray: Keyword[]
-productBrand: String
-export: Boolean
-ProductCategoryArray: ProductCategory[]
-RelatedProductArray: RelatedProduct[]
-ProductPartArray: ProductPart[]
-lastChangeDate: Date
-creationDate: Date
-endDate: Date
-effectiveDate: Date
-isCaution: Boolean
-cautionComment: String
-isCloseout: Boolean
-lineName: String
-primaryImageURL: String
-complianceInfoAvailable: Boolean
-unspscCommodityCode: String
-imprintSize: String
-defaultSetUpCharge: String
-defaultRunCharge: String
}
class MarketingPoint {
-pointType: String
-pointCopy: String
}
class Keyword {
-keyword: String
}
class ProductCategory {
-category: String
-subCategory: String
}
class RelatedProduct {
-relationType: String
-productId: String
-partId: String
}
class ProductPart {
-partId: String
-description: String[]
-countryOfOrigin: String
-ColorArray: Color[]
-primaryMaterial: String
-SpecificationArray: Specification[]
-shape: String
-ApparelSize: ApparelSize
-Dimension: Dimension
-leadTime: Integer
-unspsc: String
-gtin: String
-isRushService: Boolean
-ProductPackagingArray: Packaging[]
-ShippingPackageArray: ShippingPackage[]
-endDate: Date
-effectiveDate: Date
-isCloseout: Boolean
-isCaution: Boolean
-cautionComment: String
-nmfcCode: Decimal
-nmfcDescription: String
-nmfcNumber: String
-isOnDemand: Boolean
-isHazmat: Boolean
-primaryColor: Color
}
class Color {
-colorName: String
-hex: String
-approximatePms: String
-standardColorName: String
}
class Specification {
-specificationType: String
-SpecificationUom: String
-measurementValue: String
}
class Dimension {
-dimensionUom: String
-depth: Decimal
-height: Decimal
-width: Decimal
-weightUom: String
-weight: Decimal
}
class Packaging {
-default: Boolean
-packageType: String
-description: String
-quantity: Decimal
-dimensionUom: String
-depth: Decimal
-height: Decimal
-width: Decimal
-weightUom: String
-weight: Decimal
}
class ShippingPackage {
-packageType: String
-description: String
-quantity: Decimal
-dimensionUom: String
-depth: Decimal
-height: Decimal
-width: Decimal
-weightUom: String
-weight: Decimal
}
class ErrorMessage {
-code: Integer
-description: String
}
GetProductRequest --> GetProductResponse
GetProductResponse --> Product
Product --> MarketingPoint
Product --> Keyword
Product --> ProductCategory
Product --> RelatedProduct
Product --> ProductPart
ProductPart --> Color
ProductPart --> Specification
ProductPart --> Dimension
ProductPart --> Packaging
ProductPart --> ShippingPackage
GetProductResponse --> ErrorMessage
```
---
## Product Data 2.0.0
Source: https://docs.psrestful.com/standards/product-data-2.0.0
### **PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES**
## **Product Data 2.0.0**

#### **DOCUMENT CHANGE LOG**
| Version | Date | Reason for Change | Authors |
|---------|------------|----------------------|------------------|
| 2.0.0 | 10/23/2019 | Initial Release | See Contributors |
#### **CONTRIBUTORS**
- Paul Fleischman, Technical Lead, PCNA
- Jon Norris, VP of Operations, Starline
- Tim Dietrich, PromoStandards Consultant
- Raj Mukherjee, Director of Integrations And ERP at Hit Promotional Products
- Mike Olivieri, Manager of Integrations and Web Applications at BIC Graphic North America
- Max Stepanskiy, Executive Director, Enterprise Architecture at Advertising Specialty Institute
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the
Promotional Products Industry. This document will discuss in detail the technology required in
order to build the interface. Additionally, this document will provide sample code in order to
use the interface.
This document will assume that the reader is fluent in web based technologies, and has
knowledge of the language they plan to consume the web service in.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the
foundation for the web services protocol stack in order to provide a standards based secure
form of communication.
More information on SOAP can be found at [w3](http://www.w3.org/TR/soap12-part1/)
## PRODUCT DATA
There will be four functions available as part of this web service.
- [getProduct()](#getproduct)
- [getProductDateModified()](#getproductdatemodified)
- [getProductCloseOut()](#getproductcloseout)
- [getProductSellable()](#getproductsellable)
---
## `getProduct()`
This function provides the detailed product data based on a specific ProductId or any additional optional subset filtering provided at the request.
### REQUEST: `GetProductRequest`
| Field | Description | Data Type | Required? |
|----------------------|-----------------------------------------------------------|-----------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country (Example: CA=Canada; US=United States) | 2 STRING | TRUE |
| localizationLanguage | ISO 639-1 Alpha 2 code for Language (Example: en = English; fr = French) | 2 STRING | TRUE |
| productId | Item’s ID | 64 STRING | TRUE |
| partId | The Supplier specific PartId; commonly referred to as a sku | 64 STRING | FALSE |
| colorName | A specific part color for this product | 64 STRING | FALSE |
| [ApparelSizeArray](#apparelsize-object) | Used for returning specific apparel sizes. Array of ApparelSize objects. | ARRAY | FALSE |
### REPLY: `GetProductResponse`
| Field | Description | Data Type | Required? |
|----------------------|-----------------------------------------------------------|-----------|-----------|
| [Product](#product-object) | Product Information | OBJECT | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects. | ARRAY | FALSE |
### `Product` Object
| Field | Description | Data Type | Required? |
|-------------------------|-----------------------------------------------------------|-----------|-----------|
| productId | The associated product. | 64 STRING | TRUE |
| productName | The Supplier name for the product | 256 STRING| TRUE |
| [LocationDecorationArray](#locationdecorationarray)| An array of Location Decoration objects | OBJECT ARRAY | TRUE |
| description | Basic product description or bulleted list of descriptions. | 2048 STRING ARRAY | TRUE |
| priceExpiresDate | The date that the pricing in the ProductPriceGroupArray portion of the response expires. | ISO 8601 DATE | FALSE |
| [ProductMarketingPointArray](#productmarketingpoint-object) | Marketing points type and content. An array of MarketingPoint objects. | OBJECT ARRAY | FALSE |
| [ProductKeywordArray](#productkeyword-object) | Array of keywords often used in searching for this product. An array of ProductKeyword objects. | OBJECT ARRAY | FALSE |
| productBrand | The specific product brand | 64 STRING | FALSE |
| export | Product status for export | NILLABLE BOOLEAN | TRUE |
| [ProductCategoryArray](#productcategory-object) | The product’s categorization array. An array of ProductCategory objects. | OBJECT ARRAY | FALSE |
| [RelatedProductArray](#relatedproduct-object) | Products related to the quested product as Substitutable, CompanionSell, or Common Grouping (Family). An array of RelatedProduct objects. | OBJECT ARRAY | FALSE |
| [ProductPartArray](#productpart-object) | All part specific product data. An array of ProductPart objects. | OBJECT ARRAY | TRUE |
| lastChangeDate | The date time stamp of the most recent change to this data in ISO 8601 format | ISO 8601 DATE | TRUE |
| creationDate | The date time stamp when this products data initially became available in ISO 8601 format | ISO 8601 DATE | TRUE |
| endDate | The Date this Product expires from Supplier availability in ISO 8601 format | ISO 8601 DATE | FALSE |
| effectiveDate | The Date this Product initially becomes available from the Supplier in ISO 8601 format | ISO 8601 DATE | FALSE |
| isCaution | Cautionary status to review for specific warnings about using product data. | NILLABLE BOOLEAN | TRUE |
| cautionComment | Product cautionary comments | 1024 STRING | FALSE |
| isCloseout | Indicates if the product is a closeout | NILLABLE BOOLEAN | TRUE |
| lineName | Line Name / Division to which this product belongs | 64 STRING | FALSE |
| primaryImageURL | The URL of the product’s primary image. Any valid URL can be returned including prefixes like http and ftp. | 1024 STRING | FALSE |
| [ProductPriceGroupArray](#productpricegroup-object) | Marketing-oriented pricing information. Represented as an array of Product Price objects. | OBJECT ARRAY | FALSE |
| complianceInfoAvailable | Indicates if there is information about this product via the Product Safety & Compliance service. | NILLABLE BOOLEAN | FALSE |
| unspscCommodityCode | The United Nations Standard Products and Services Code® (UNSPSC®) that best describes this product. Note that the enumerated values are the UNSPSC "Commodity" codes. For more information, refer to https://www.unspsc.org | INT | FALSE |
| imprintSize | The imprint Size | 256 STRING | FALSE |
| defaultSetUpCharge | The default setup charge for this product. Can be a textual description. | 1024 STRING | FALSE |
| defaultRunCharge | The default RUN charge for this product. Can be a textual description. | 1024 STRING | FALSE |
| FobPointArray | An array of FOB points object | ARRAY | TRUE |
### `LocationDecorationArray`
An array of `LocationDecoration` objects that provide information about the decoration locations available for a product.
### `LocationDecoration` Object
| Field | Description | Data Type | Required? |
|--------------------------------|---------------------------------------------------------------|------------|-----------|
| locationName | The name of the decoration location. | 255 STRING | TRUE |
| maxImprintColors | The maximum number of imprint colors available, based on a decoration location and type. | INTEGER | FALSE |
| decorationName | The name of the decoration method or type. | 64 STRING | TRUE |
| locationDecorationComboDefault | Indicates if this is the default location for the decoration. | BOOLEAN | TRUE |
| priceIncludes | Indicates if the price includes this decoration. | BOOLEAN | TRUE |
### `ProductCategory` Object
| Field | Description | Data Type | Required? |
|-------------------------|-----------------------------------------------------------|------------|-----------|
| category | Product category | 256 STRING | TRUE |
| subCategory | Product subcategory | 256 STRING | FALSE |
### `ProductMarketingPoint` Object
| Field | Description | Data Type | Required? |
|-------------------------|-----------------------------------------------------------|-----------|-----------|
| pointType | Basic category or type of marketing point being made. e.g. Highlights, Size, Safety | 64 STRING | FALSE |
| pointCopy | Marketing bullet or point copy | 1024 STRING | TRUE |
### `ProductKeyword` Object
| Field | Description | Data Type | Required? |
|---------------------|-----------------------------------------------------------|------------|-----------|
| keyword | A product keyword commonly utilized for search or other functions | 64 STRING | TRUE |
### `RelatedProduct` Object
| Field | Description | Data Type | Required? |
|----------------|--------------------------------------------------|-----------|-----------|
| relationType | The relationship type between two products. | 64 STRING | TRUE |
| productId | Product Id associated with the related product. | 64 STRING | TRUE |
| partId | Part specific Id associated with the related product. | 64 STRING | FALSE |
### `ProductPriceGroup` Object
| Field | Description | Data Type | Required? |
|----------------|------------------------------------------------------------------|-----------|-----------|
| groupName | The relationship type between two products. | 64 STRING | FALSE |
| currency | Currency for this price group based on [iso4217](http://www.isotc211.org/iso4217/) | 3 FACIT | TRUE |
| description | Descrition of the group price | 64 STRING | FALSE |
| [ProductPriceArray](#productprice-object) | Array of ProductPrice Objects | ARRAY | FALSE |
### `ProductPrice` Object
| Field | Description | Data Type | Required? |
|----------------|--------------------------------------------------|-----------|-----------|
| quantityMin | minimun quanity for this price to kick in | INTEGER | TRUE |
| quantityMax | maximum quanity for this price | INTEGER | FALSE |
| price | List Price | DECIMAL | TRUE |
| discountCode | Discount available | STRING | FALSE |
### `ProductPart`
An array of `ProductPart` objects that provide detailed information about the specific parts of a product.
### `ProductPart` Object
| Field | Description | Data Type | Required? |
|-----------------------|--------------------------------------------------|-----------|-----------|
| partId | The part Id | 64 STRING | TRUE |
| description | Description or bulleted list of descriptions. | 2048 STRING (ARRAY) | FALSE |
| countryOfOrigin | Country of Origin in ISO 3166-1 Alpha 2 code format (Example: CA=Canada; US=United States) | 2 STRING | FALSE |
| [ColorArray](#color-object) | Information about the part colors. An array of `Color` objects. | OBJECT ARRAY | FALSE |
| primaryMaterial | Primary material of construction | 64 STRING | FALSE |
| [SpecificationArray](#specification-object) | Specifications OBJECT | ARRAY | FALSE |
| shape | General shape | 64 STRING | FALSE |
| [ApparelSize](#apparelsize-object) | Used to describe an apparel size | OBJECT | FALSE |
| [Dimension](#dimension-object) | Physical dimensions and weight | OBJECT | FALSE |
| leadTime | Lead time in days | INT | FALSE |
| unspsc | United Nations Standard Products and Services Code (UNSPSC) | 8 STRING | FALSE |
| gtin | Global Trade Item Number (GTIN) | 14 STRING | FALSE |
| isRushService | Rush service | NILLABLE BOOLEAN | TRUE |
| [ProductPackagingArray](#productpackaging-object) | Packaging option details; e.g. `Gift Box`, `Cello Pack`, `Sleeve`. An array of `ProductPackaging` objects. | OBJECT ARRAY | FALSE |
| [ShippingPackageArray](#shippingpackage-object) | Shipping package option details for the partPackaging Default package type; e.g. `Carton`, `Box`, `Pallet`. An array of `ShippingPackage` objects. | OBJECT ARRAY | FALSE |
| endDate | The date this part expires from supplier availability in ISO 8601 format | ISO 8601 DATE | FALSE |
| effectiveDate | The date this part initially becomes available from the supplier in ISO 8601 format | ISO 8601 DATE | FALSE |
| isCloseout | Indicates if a closeout | NILLABLE BOOLEAN | TRUE |
| isCaution | Cautionary status to review for specific warnings about using product data | NILLABLE BOOLEAN | TRUE |
| cautionComment | Cautionary comments | 1024 STRING | FALSE |
| nmfcCode | National Motor Freight Classification Code | DECIMAL | FALSE |
| nmfcDescription | National Motor Freight Classification Description | 1024 STRING | FALSE |
| nmfcNumber | National Motor Freight Classification Number | 64 STRING | FALSE |
| isOnDemand | Manufactured on demand / Made to order | NILLABLE BOOLEAN | TRUE |
| isHazmat | Contains hazardous material. A nil value indicates this it is unknown or the data is not available by the supplier. | NILLABLE BOOLEAN | TRUE |
| [primaryColor](#color-object) | The "go to market" color. Represented as a Color Object. | OBJECT | FALSE |
### Color Object
| Field | Description | Data Type | Required? |
|---------------------|-----------------------------------------------------------------------------------------------|------------|-----------|
| colorName | Supplier specific color name. Go to market color. | 64 STRING | TRUE |
| hex | The HEX code representation. This is not a supplement for product color but data used for generating web user interfaces. | 64 STRING | FALSE |
| approximatePms | The PMS code representation. This is not a supplement for product color. | 64 STRING | FALSE |
| standardColorName | The standard color that best describes this color. Eventually may become an enumeration. | 64 STRING | FALSE |
### ApparelSize Object
| Field | Description | Data Type | Required? |
|----------------|-----------------------------------------------------------|-----------|-----------|
| apparelStyle | Apparel style | 64 STRING | TRUE |
| labelSize | The apparel items tagged size. e.g. XS, S, M, L, etc. | 6 STRING | TRUE |
| customSize | Used to communicate custom size when labelSize is CUSTOM. | 64 STRING | FALSE |
### Dimension Object
| Field | Description | Data Type | Required? |
|----------------|----------------------------------------------------------------------------------|-----------|-----------|
| dimensionUom | The dimensional unit of measure | 2 STRING | TRUE |
| depth | The depth/length of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| height | The height of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| width | The width of the part using the unit of measure specified in dimensionUom | DECIMAL | FALSE |
| weightUom | The weight unit of measure for the part | 2 STRING FACIT | TRUE |
| weight | The weight of the part using the unit of measure specified in weightUom | DECIMAL | FALSE |
### ProductPackaging Object
An array of `ProductPackaging` objects that provide details about the packaging options available for a product, such as Gift Box, Cello Pack, Sleeve, etc.
### ProductPackaging Object
| Field | Description | Data Type | Required? |
|------------------------|--------------------------------------------------------------------------|-------------|-----------|
| packageType | The specific type of product packaging, e.g., Box, Carton, etc. | 256 STRING | TRUE |
| description | Description or details of the product packaging. | 2048 STRING | FALSE |
| quantity | The partId unit quantity included within this specific packaging option. | DECIMAL | TRUE |
| dimensionUom | The unit of measure for the package dimensions. | 2 STRING | TRUE |
| depth | The depth/length of the package using the specified unit of measure. | DECIMAL | FALSE |
| height | The height of the package using the specified unit of measure. | DECIMAL | FALSE |
| width | The width of the package using the specified unit of measure. | DECIMAL | FALSE |
### ShippingPackage Object
An array of `ShippingPackage` objects that provide details about the shipping packaging options available for a product, such as Carton, Box, Pallet, etc.
### ShippingPackage Object
| Field | Description | Data Type | Required? |
|------------------------|--------------------------------------------------|-----------|-----------|
| packageType | The shipping package specific type for this shipping package e.g. Box, Carton, etc. | 256 STRING | TRUE |
| description | The shipping package specific description. | 2048 STRING | FALSE |
| quantity | The partId unit quantity included within this specific shipping packaging option. | DECIMAL | TRUE |
| dimensionUom | The weight unit of measure for the packageType. | 2 STRING | TRUE |
| depth | The depth/length of the shipping package in the dimensionUom unit of measure. | DECIMAL | FALSE |
### Specification Object
| Field | Description | Data Type | Required? |
|---------------------|--------------------------------------------------|-----------|-----------|
| specificationType | The type of specification | 64 STRING | TRUE |
| SpecificationUom | Specification unit of measure | 64 STRING | TRUE |
| measurementValue | Specification Measurement Value | 64 STRING | TRUE |
---
## `getProductDateModified()`
This function provides the list of Product Ids and optional Part Ids for all items which have changed since the date supplied in the request.
### REQUEST: `GetProductDateModifiedRequest`
| Field | Description | Data Type | Required? |
|------------------|-----------------------------------------------------------|-----------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
| changeTimeStamp | Beginning date time since last change in UTC | ISO 8601 DATE | TRUE |
### REPLY: `GetProductDateModifiedResponse`
| Field | Description | Data Type | Required? |
|----------------------------|-----------------------------------------------------------|-----------|-----------|
| [ProductDateModifiedArray](#productdatemodified-object) | Array listing of all Product and Part specific Ids which have changed since the reference date requested (lastChangeDate). An array of ProductChange objects. | OBJECT ARRAY | TRUE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects. | ARRAY | FALSE |
### `ProductDateModified` Object
| Field | Description | Data Type | Required? |
|-------------|-----------------------------------|-----------|-----------|
| productId | The associated product | 64 STRING | TRUE |
| partId | The associated part | 64 STRING | FALSE |
---
## `getProductCloseOut()`
This function provides a list of Product Ids and optional Part Ids for all items which currently have an `isCloseOut` value of TRUE.
### REQUEST: `GetProductCloseOutRequest`
| Field | Description | Data Type | Required? |
|------------------|-----------------------------------------------------------|-----------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
### REPLY: `GetProductCloseOutResponse`
| Field | Description | Data Type | Required? |
|----------------------------|-----------------------------------------------------------|-----------|-----------|
| [ProductCloseOutArray](#productcloseout-object) | All product and part specific Ids which are on closeout. An array of `ProductCloseOut` objects. | OBJECT ARRAY | FALSE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects. | ARRAY | FALSE |
### `ProductCloseOut` Object
| Field | Description | Data Type | Required? |
|-------------|-----------------------------------|-----------|-----------|
| productId | The associated product | 64 STRING | TRUE |
| partId | The associated part | 64 STRING | FALSE |
---
## `getProductSellable()`
This function provides a list of Product Ids and optional Part Ids along with their status of available to sell (Sellable TRUE or FALSE).
### REQUEST: `GetProductSellableRequest`
| Field | Description | Data Type | Required? |
|---------------------|-----------------------------------------------------------|-----------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | 64 STRING | TRUE |
| id | The customer Id or any other agreed upon Id. | 64 STRING | TRUE |
| password | The password associated with the Id | 64 STRING | FALSE |
| productId | The associated product | 64 STRING | FALSE |
| partId | The associated part | 64 STRING | FALSE |
| localizationCountry | ISO 3166-1 Alpha 2 code for Country (Example: CA=Canada) | 2 STRING | TRUE |
### REPLY: `GetProductSellableResponse`
| Field | Description | Data Type | Required? |
|-----------------------|-----------------------------------------------------------|-----------|-----------|
| [ProductSellableArray](#productsellable-object) | Array listing of all product and part specific Ids along with sellable status. An array of ProductSellable objects. | OBJECT ARRAY | TRUE |
| [ServiceMessageArray](#servicemessage-object) | An array of ServiceMessage objects. | ARRAY | FALSE |
### `ProductSellable` Object
| Field | Description | Data Type | Required? |
|----------------|-----------------------------------|-----------|-----------|
| productId | The associated product | 64 STRING | TRUE |
| partId | The associated part | 64 STRING | FALSE |
| culturePoint | The culture point | 64 STRING | FALSE |
### `ServiceMessage` Object
| Field | Description | Data Type | Required? |
|----------------|-----------------------------------|-----------|-----------|
| code | The numerical value of the code | INTEGER | TRUE |
| description | Response for any message requiring notification to requestor | 256 STRING | TRUE |
| severity | The severity of the message. Values are enumerated: \{Error, Information, Warning\} | FACET 256 STRING | TRUE |
### Enumerated Type Restrictions
| Field | Description | Data Type | Values |
|------------------ |-----------------------------------------------------------|---------------- | ----------- |
| wsVersion | The Standard Version of the Web Service being referenced | 64 STRING FACIT | 2.0.0 |
| dimensionUom | The dimensional unit of measure | 64 STRING FACIT | MM (Millimeter)
CM (Centimeter)
MR (Meter)
IN (Inch)
FT (Feet)
YD (Yards) |
| weightUom | The weight unit of measure | 2 STRING FACIT | ME (Milligram)
KG (Kilogram)
OZ (Ounce)
LB (Pound) |
| relationType | The relationship type between the two products | 64 STRING FACIT | Substitute
Companion Sell
Common Grouping |
| specificationType | List of possible values. The type of specification. | 64 STRING FACIT | Length
Thickness
Radius
Volume
Capacity
Memory
Data Ports
Capacitance
Voltage
Point Size
Sheet Size
Sheet Count
Pockets
Inseam
Bust
Chest
Waist
Hips
Cup
Rise
Neck
Thigh
Shoulders
Sleeve
Device Size |
| apparelStyle | List of possible values. e.g. Ladies, Men, Youth, etc. | 64 STRING FACIT | Unisex
Youth
Girls
Boys
Womens
WomensTall
MensMensTall |
| labelSize | List of possible values. The apparel items tagged size. e.g. XS, S, M, XL, etc. | 4 STRING FACIT | OSFA (One Size Fits All)
4XS (4 Extra Small)
3XS (3 Extra Small)
2XS (2 Extra Small)
XS (Extra Small)
S
M
L
XL (Extra Large)
2XL (2 Extra Large)
3XL (3 Extra Large)
4XL (4 Extra Large)
5XL (5 Extra Large)
CUSTOM |
**Standardized Codes:** The range of 100-199 has been reserved for standardized error codes.
The number 999 has been reserved for an error codes that is a “General Error - Contact System Service Provider”
| Code | Description |
| ------ | ---------------------------- |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 130 | Product Id not found |
| 135 | Product color not found |
| 140 | Part Id not found |
| 145 | Part color not found |
| 150 | Part size not found |
| 999 | General Error – Contact the System Service Provider |
## DATA MINING / ABUSE GUIDELINE
The use of services detailed within this specification are to be conducted within the current
guidelines of the “Interface Data Use Guidelines Standards”
## VALIDATION OF SERVICES
Before you publish your endpoint, please ensure that it adheres to the promostandards.org spec by
using the web service validation tool.
https://services.promostandards.org/webserviceValidator/home
Select the service, version, method and input your endpoint. If the endpoint is correct you should
receive a message of:
"The XML response is valid."
## Diagrams
```mermaid
classDiagram
class GetProductRequest {
-wsVersion: String
-id: String
-password: String
-localizationCountry: String
-localizationLanguage: String
-productId: String
-partId: String
-colorName: String
-ApparelSizeArray: ApparelSize[]
}
class ApparelSize {
-apparelStyle: String
-labelSize: String
-customSize: String
}
class GetProductResponse {
-Product: Product
-ServiceMessageArray: ServiceMessage[]
}
class Product {
-productId: String
-productName: String
-LocationDecorationArray: LocationDecoration[]
-description: String[]
-priceExpiresDate: Date
-ProductMarketingPointArray: MarketingPoint[]
-ProductKeywordArray: Keyword[]
-productBrand: String
-export: Boolean
-ProductCategoryArray: ProductCategory[]
-RelatedProductArray: RelatedProduct[]
-ProductPartArray: ProductPart[]
-lastChangeDate: Date
-creationDate: Date
-endDate: Date
-effectiveDate: Date
-isCaution: Boolean
-cautionComment: String
-isCloseout: Boolean
-lineName: String
-primaryImageURL: String
-ProductPriceGroupArray: ProductPriceGroup[]
-complianceInfoAvailable: Boolean
-unspscCommodityCode: String
-imprintSize: String
-defaultSetUpCharge: String
-defaultRunCharge: String
-FobPointArray: FobPoint[]
}
class LocationDecoration {
-locationName: String
-maxImprintColors: Integer
-decorationName: String
-locationDecorationComboDefault: Boolean
-priceIncludes: Boolean
}
class MarketingPoint {
-pointType: String
-pointCopy: String
}
class Keyword {
-keyword: String
}
class ProductCategory {
-category: String
-subCategory: String
}
class RelatedProduct {
-relationType: String
-productId: String
-partId: String
}
class ProductPart {
-partId: String
-description: String[]
-countryOfOrigin: String
-ColorArray: Color[]
-primaryMaterial: String
-SpecificationArray: Specification[]
-shape: String
-ApparelSize: ApparelSize
-Dimension: Dimension
-leadTime: Integer
-unspsc: String
-gtin: String
-isRushService: Boolean
-ProductPackagingArray: Packaging[]
-ShippingPackageArray: ShippingPackage[]
-endDate: Date
-effectiveDate: Date
-isCloseout: Boolean
-isCaution: Boolean
-cautionComment: String
-nmfcCode: Decimal
-nmfcDescription: String
-nmfcNumber: String
-isOnDemand: Boolean
-isHazmat: Boolean
-primaryColor: Color
}
class Color {
-colorName: String
-hex: String
-approximatePms: String
-standardColorName: String
}
class Specification {
-specificationType: String
-SpecificationUom: String
-measurementValue: String
}
class Dimension {
-dimensionUom: String
-depth: Decimal
-height: Decimal
-width: Decimal
-weightUom: String
-weight: Decimal
}
class Packaging {
-default: Boolean
-packageType: String
-description: String
-quantity: Decimal
-dimensionUom: String
-depth: Decimal
-height: Decimal
-width: Decimal
-weightUom: String
-weight: Decimal
}
class ShippingPackage {
-packageType: String
-description: String
-quantity: Decimal
-dimensionUom: String
-depth: Decimal
-height: Decimal
-width: Decimal
-weightUom: String
-weight: Decimal
}
class ProductPriceGroup {
-groupName: String
-currency: String
-description: String
-ProductPriceArray: ProductPrice[]
}
class ProductPrice {
-quantityMax: Integer
-quantityMin: Integer
-price: Decimal
-discountCode: String
}
class FobPoint {
-fobId: String
-fobPostalCode: String
-fobCity: String
-fobState: String
-fobCountry: String
}
class ServiceMessage {
-code: Integer
-description: String
-severity: String
}
GetProductRequest --> GetProductResponse
GetProductResponse --> Product
Product --> LocationDecoration
Product --> MarketingPoint
Product --> Keyword
Product --> ProductCategory
Product --> RelatedProduct
Product --> ProductPart
ProductPart --> Color
ProductPart --> Specification
ProductPart --> Dimension
ProductPart --> Packaging
ProductPart --> ShippingPackage
Product --> ProductPriceGroup
ProductPriceGroup --> ProductPrice
Product --> FobPoint
GetProductResponse --> ServiceMessage
```
---
## Purchase Order 1.0.0
Source: https://docs.psrestful.com/standards/purchase-order-1.0.0
# PROMOTIONAL PRODUCTS DATA INTERFACE SPECIFICATION FOR WEB SERVICES

## Purchase Order Service
**Version**: 1.0.0
**Release Date**: 2018-07-01
## **Document Change Log**
| Version | Date | Reason for Change | Author |
|---------|------------|----------------------------------------------------------------|----------------------------|
| 1.0.0 | 2018-07-01 | v1.0.0 publish | See contributors section |
## Contributors
The following have contributed to the creation of this specification:
- Eric Shonebarger, CIO Hit Promotional Products, Inc
- Jon Norris, VP of Operations Starline
- Paul Fleischman, Technical Lead PCNA
- Dustin Kessler, Enterprise Architect, Staples Promotional Products
- Steve Luisser, VP of R&D, Essent
## Abstract and Recommended Audience
This document describes the technologies for integration of suppliers and distributors in the Promotional Products Industry. This document will discuss in detail the technology required in order to build the interface. Additionally, this document will provide sample code in order to use the interface.
This document will assume that the reader is fluent in web based technologies, and has knowledge of the language they plan to consume the web service in.
## Background Information
All specifications will be built using the Simple Object Access Protocol (SOAP) over HTTPS as the foundation for the web services protocol stack in order to provide a standards based secure form of communication.
More information on SOAP can be found at [w3](http://www.w3.org/TR/soap12-part1/)
## **Service Details:**
- [getSupportedOrderTypes](#function-getsupportedordertypes)
- [sendPO](#function-sendpo)
## Function: getSupportedOrderTypes()
This function returns the supported Order Types the vendor accepts.
### Request: `getSupportedOrderTypesRequest`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------|-----------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customerId or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customerId. | STRING | VARCHAR(64) | FALSE |
---
### Response: `getSupportedOrderTypesResponse`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------------------|----------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| SupportedOrderTypes[] | An array of supported order types for this supplier. Signifies which order types the supplier supports. Values are enumerated: “Blank”, “Sample”, “Simple” “Configured” | ARRAY | ARRAY | FALSE |
| [ServiceMessageArray](#object-servicemessage) | An array of ServiceMessage objects. | OBJECT | OBJECT | FALSE |
### Function: sendPO()
This function will send a configured purchase order to a vendor. The purchase order is designed to work in conjunction with data from the Promostandards Product Pricing and Configuration web service. More information on Product Pricing and Configuration can be found at http://promostandards.org.
Absolutely! Here's the markdown documentation based on the provided details:
### Request: `sendPORequest`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------|------------------------------------------------------------------------|----------------|---------------|-----------|
| wsVersion | The Standard Version of the Web Service being referenced. Values are enumerated \{1.0.0\} | STRING | VARCHAR(64) | TRUE |
| id | The customer Id or any other agreed upon Id. | STRING | VARCHAR(64) | TRUE |
| password | The password associated with the customer Id. | STRING | VARCHAR(64) | FALSE |
| [PO](#po-object) | The object containing the Purchase Order | OBJECT | OBJECT | TRUE |
### PO Object
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|---------------|-----------|
| orderType | The type of data transferred in the request with enumerated values: Blank, Sample, Simple, Configured | ENUMERATED STRING | VARCHAR(64) | TRUE |
| orderNumber | Purchase order number | STRING | VARCHAR(64) | TRUE |
| orderDate | The date and time of the purchase order | DATE | DATE | TRUE |
| lastModified | The date and time the purchase order was last modified | DATE | DATE | FALSE |
| totalAmount | The total dollar amount of the purchase order | DOUBLE | DECIMAL(12,4) | TRUE |
| paymentTerms | The terms of the purchase order. i.e. NET15, NET30, etc. | STRING | STRING | FALSE |
| rush | Used to indicate a rush on the purchase order | BOOLEAN | BOOLEAN | TRUE |
| currency | The currency the purchase order is transacted in ISO4217 format | STRING | VARCHAR(3) | TRUE |
| [DigitalProof](#po-object) | An object containing preproduction digital proof information | OBJECT | OBJECT | FALSE |
| [OrderContactArray](#object-contact) | An array of contact information | ARRAY | ARRAY | FALSE |
| [ShipmentArray](#object-shipment) | Any array of purchase order shipments | OBJECT ARRAY | OBJECT ARRAY | TRUE |
| [LineItemArray](#object-lineitem) | An array of purchase order line items | ARRAY | ARRAY | TRUE |
| termsAndConditions | The terms and conditions for this purchase order. Information that is order specific or information dealing with the configuration or shipment of the order should not be entered here. | STRING | VARCHAR(MAX) | TRUE |
| salesChannel | The sales channel | STRING | VARCHAR(64) | FALSE |
| promoCode | The promotion code | STRING | VARCHAR(64) | FALSE |
| [TaxInformationArray](#object-taxinformation) | An array of TaxInformation objects related to calculating taxes | ARRAY | ARRAY | FALSE |
### Object: `DigitalProof`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| required | A Boolean value set to TRUE indicates a proof is required for this purchase order; the value is FALSE otherwise. | BOOLEAN | BOOLEAN | TRUE |
| [DigitalProofAddressesArray](#object-digitalproofaddress) | An array containing DigitalProofAddress objects. | ARRAY | ARRAY | TRUE |
### Object: `DigitalProofAddress`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------|---------------------------------------------------------------------------------------|----------------|---------------|-----------|
| type | The type of address of the digital proof. Values are enumerated: Email, Webservice | STRING | VARCHAR(64) | TRUE |
| email | The address that the digital proof should be sent to prior to production. | STRING | VARCHAR(128) | TRUE |
| lineItemGroupingId | What line item group does this proof address link to | INT | INT | TRUE |
### Object: `TaxInformation`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|------------------------------------------------------------------------------------------------------------------------------------|----------------------|---------------|-----------|
| taxId | The purchasers tax identifier | STRING | VARCHAR(64) | TRUE |
| taxType | The type of tax the identifier applies to. Values are enumerated: “Hst-Gst”, “Pst”, “SalesTax” | ENUMERATED STRING | VARCHAR(64) | TRUE |
| taxExempt | Tax Except | BOOLEAN | BOOLEAN | TRUE |
| taxJurisdiction | Tax Jurisdictions | UNBOUNDED STRING ARRAY | VARCHAR(64) | TRUE |
| taxAmount | The amount of tax for this purchase order | DECIMAL | DECIMAL | FALSE |
### Object: `LineItem`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|---------------|-----------|
| lineNumber | The line number of the line item | STRING | VARCHAR(64) | TRUE |
| description | The description of the line item. For simple order type (not using a configuration), use this field to explain the details. | STRING | VARCHAR(MAX) | TRUE |
| lineType | The type of order; values are enumerated: “New” –A new purchase order with no prior order reference, “Repeat” —An exact repeat of a previous purchase order with the vendor, “Reference” –An order that has the same artwork as a previous order. | ENUMERATED STRING | VARCHAR(64) | TRUE |
| [Quantity](#object-quantity) | The quantity object that contains the value and unit of measure | OBJECT | OBJECT | FALSE |
| fobId | Used to indicate the FOB point. Use fobId from the supplier’s Product Pricing and Configuration Service to populate this information. | STRING | VARCHAR(64) | FALSE |
| [ToleranceDetails](#object-tolerancedetails) | The object containing how tolerant this line is to overrun and underruns. | OBJECT | OBJECT | TRUE |
| allowPartialShipments | Allow partial shipments of this line item | BOOLEAN | BOOLEAN | TRUE |
| unitPrice | The unit price of the line item | DOUBLE | DECIMAL(12,4) | FALSE |
| lineItemTotal | The total for the line item | DOUBLE | DECIMAL(12,4) | TRUE |
| requestedShipDate | The date the line item is requested to ship from the FOB point. | DATE | DATE | FALSE |
| requestedInHands | The date the line item is requested to arrive at the shipping destination | DATE | DATE | FALSE |
| referenceSalesQuote | The sales quote number associated with this purchase order line (if applicable). | VARCHAR(64) | VARCHAR(64) | FALSE |
| [Program](#object-program) | Program pricing information. | OBJECT | OBJECT | FALSE |
| endCustomerSalesOrder | The distributor’s order number provided to the end customer | VARCHAR(64) | VARCHAR(64) | FALSE |
| productId | The manufacturer’s product id associated with the configuration data. | STRING | VARCHAR(64) | FALSE |
| customerProductId | The distributor’s product id | STRING | VARCHAR(64) | FALSE |
| lineItemGroupingId | An identifier that allows configuration data to be spread out among multiple purchase order lines. Keep lineItemGroupingID unique when referencing the same product on the purchase order. Any change to the product, location, decoration, or artwork should produce a unique lineItemGroupingID to the purchase order. | INT | INT | FALSE |
| [PartArray](#object-part) | An array of product part information. This array should be populated with information from the supplier’s PromoStandards Product Pricing and Configuration service | ARRAY | ARRAY | FALSE |
| [Configuration](#object-configuration) | An object containing line item configuration data | OBJECT | OBJECT | FALSE |
### Object: `ToleranceDetails`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|---------------|-----------|
| tolerance | An enumerator specifying the quantity tolerance allowed: AllowOverRun, AllowUnderrun, AllowOverrunOrUnderrun, ExactOnly. Specifying AllowOverRun, AllowUnderrun or AllowOverrunOrUnderrun without a value and uom will result in the supplier’s discretion. | ENUMERATED STRING | VARCHAR(64) | TRUE |
| value | The overrun/underrun permitted. This element is ignored if ExactOnly is specified for tolerance. | DECIMAL | DECIMAL(12,4) | FALSE |
| uom | The unit of measure for value element; enumerated values are: \{Percent, Quantity\}. This element is ignored if ExactOnly is specified for tolerance. | ENUMERATED STRING | VARCHAR(10) | FALSE |
### Object: `Program`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|---------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| id | The program id associated with this purchase order line (if applicable). | VARCHAR(64) | VARCHAR(64) | FALSE |
| name | The program name associated with this purchase order line (if applicable). | VARCHAR(64) | VARCHAR(64) | FALSE |
### Object: `Quantity`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------|---------------------------------------------------------------------------------------------------|--------------------|---------------|-----------|
| value | The quantity value | DECIMAL | DECIMAL(12,4) | TRUE |
| uom | The unit of measure; values are enumerated. Values are: \{BX, CA, DZ, EA, KT, PR, PK, RL, ST, SL, TH\}. Enumerations: BX - Box, CA - Case, DZ - Dozen, EA - Each, KT - Kit, PR - Pair, PK - Package, RL - Roll, ST - Set, SL - Sleeve, TH - Thousand | ENUMERATED STRING | VARCHAR(2) | TRUE |
### Object: `Configuration`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|---------------|-----------|
| referenceNumber | The previous order number that this purchase order is referencing | STRING | VARCHAR(64) | FALSE |
| referenceNumberType | The type of the prior order reference; values are enumerated: “PurchaseOrder”,”SalesOrder”, “JobOrWorkOrder” | ENUMERATED STRING | VARCHAR(64) | FALSE |
| preProductionProof | Indicates that this line item is for a pre-production proof | BOOLEAN | BOOLEAN | TRUE |
| [ChargeArray](#object-charge) | An array of charge information. This array should be populated with information from the supplier’s PromoStandards Product Pricing and Configuration service | ARRAY | ARRAY | FALSE |
| [LocationArray](#object-location) | An array of Decoration Location Information. This array should be populated with information from the supplier’s PromoStandards Product Pricing and Configuration service | ARRAY | ARRAY | FALSE |
### Object: `Part`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------------------|----------------------------------------------------------------------------------------------------------------------------------|--------------------|----------------|-----------|
| partGroup | An identifier that links common line item parts together | STRING | VARCHAR(64) | FALSE |
| partId | The part Id from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(64) | TRUE |
| customerPartId | How the part is being represented to the distributor’s customer | STRING | VARCHAR(64) | FALSE |
| customerSupplied | The part will be supplied by the customer or another entity other than the supplier | BOOLEAN | BOOLEAN | TRUE |
| description | The description from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(MAX) | FALSE |
| [Quantity](#object-quantity) | The quantity object that contains the value and unit of measure | OBJECT | OBJECT | TRUE |
| locationLinkId | An array of location link Ids. This links the part to its configured locations | ARRAY OF INTEGERS | INT[] | FALSE |
| unitPrice | The price of the part being referenced | DECIMAL | DECIMAL(12,4) | FALSE |
| extendedPrice | The unitPrice multiplied by the Quantity value | DECIMAL | DECIMAL(12,4) | FALSE |
| [ShipmentLinkArray](#object-shipmentlink) | Link this part and a specified quantity to one or many shipments | OBJECT ARRAY | OBJECT ARRAY | FALSE |
### Object: `ShipmentLink`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------------|----------------------------------------------------------------------|-----------------|---------------|-----------|
| shipmentId | The shipment Id | INT | INT | TRUE |
| [Quantity](#object-quantity) | The quantity object that contains the value and unit of measure | OBJECT | OBJECT | TRUE |
### Object: `Charge`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-----------------|---------------------------------------------------------------------------------------------------------------------------------|----------------------|---------------|-----------|
| chargeId | The chargeId from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(64) | TRUE |
| chargeName | The chargeName from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(128) | FALSE |
| description | The charge description from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(MAX) | FALSE |
| chargeType | The type of charge. Values are enumerated \{Freight, Order, Run, Setup\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
| [Quantity](#object-quantity) | The quantity object that contains the value and unit of measure | OBJECT | OBJECT | TRUE |
| unitprice | The price of the charge being referenced | DECIMAL | DECIMAL(12,4) | FALSE |
| extendedPrice | The unitPrice multiplied by the Quantity value | DECIMAL | DECIMAL(12,4) | FALSE |
### Object: `Location`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| locationLinkId | An identifier to be used within the Part Array to link configured locations to a part. Due to different colors and sizes, identical locations may need to be decorated differently | INT | INT | TRUE |
| locationId | The locationId from the supplier’s PromoStandards Product Pricing and Configuration service | INT | INT | TRUE |
| locationName | The locationName from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(128) | FALSE |
| [DecorationArray](#object-decoration)| An array of product decoration information | ARRAY | ARRAY | TRUE |
### Object: `Decoration`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| decorationId | The decorationId from the supplier’s PromoStandards Product Pricing and Configuration service | INT | INT | TRUE |
| decorationName | The decorationName from the supplier’s PromoStandards Product Pricing and Configuration service | STRING | VARCHAR(128) | FALSE |
| [Artwork](#object-artwork) | The object containing the details of the artwork | OBJECT | OBJECT | TRUE |
### Object: `Artwork`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| refArtworkId | A pre-shared artwork Id that can be used by the supplier to find and reference the artwork | STRING | VARCHAR(64) | FALSE |
| description | A textual description of the artwork being provided | STRING | VARCHAR(MAX) | FALSE |
| [Dimensions](#object-dimensions) | The dimensions of the artwork | OBJECT | OBJECT | FALSE |
| [ArtworkFileArray](#object-artworkfile) | An array of artwork file data | ARRAY | ARRAY | FALSE |
| instructions | Any instructions regarding the processing or modification of artwork. Adding instructions will cause delays in processing | STRING | VARCHAR(MAX) | FALSE |
| [Layers](#object-layers) | An object that explains how the artwork layers or stops will be handled | OBJECT | OBJECT | FALSE |
| [TypesetArray](#object-typeset) | An array of typeset data | ARRAY | ARRAY | FALSE |
| totalStitchCount | The total stitch count for the specified embroidery art | INT | INT | FALSE |
### Object: `ArtworkFile`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|----------------------|---------------|-----------|
| fileName | The file name and extension only of the artwork | STRING | VARCHAR(256) | TRUE |
| fileLocation | The exact path/location to the artwork | STRING | VARCHAR(1024) | TRUE |
| transportMechanism | The mechanism that will be used to transport the artwork; values are enumerated: “Email”, “Url”, “Ftp”, “ArtworkToFollow” | ENUMERATED STRING | VARCHAR(64) | TRUE |
| artworkType | The purpose of the artwork file; values are enumerated: “ProductionReady”, “VirtualProof”, “SupplierArtTemplate”, “NonProductionReady” | ENUMERATED STRING | VARCHAR(64) | TRUE |
### Object: `Dimensions`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|-----------------|-----------|
| geometry | The geometry of the decoration. Values are enumerated: \{Circle, Rectangle, Other\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
| useMaxLocationDimensions | Use the maximum allowed imprint dimensions for this location. If this is set to true, all other dimension information is ignored except for geometry | BOOLEAN | BOOLEAN | TRUE |
| height | The height of the artwork; leave blank if the imprint is not rectangular | DECIMAL | DECIMAL(12,4) | FALSE |
| width | The width of artwork; leave blank if the imprint is not rectangular | DECIMAL | DECIMAL(12,4) | FALSE |
| diameter | The diameter of the artwork; leave blank if the imprint is not circular | DECIMAL | DECIMAL(12,4) | FALSE |
| uom | The unit of measure for the decoration area in ISO 20022 [ISO 20022 UOM](https://www.iso20022.org/standardsrepository/public/wqt/Description/mx/dico/codesets/_Y4XF0tp-Ed-ak6NoX_4Aeg_385163498) | ENUMERATED STRING | VARCHAR(64) | FALSE |
### Object: `Layers`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------------|---------------------------------------------------------------------|------------------|-----------------|-----------|
| colorSystem | The color system to use: Cmyk, Other, Pms, Rgb, Thread | ENUMERATED STRING| ENUMERATED STRING| TRUE |
| [LayerOrStopArray](#object-layerorstop) | An array of layers for the artwork | OBJECT ARRAY | OBJECT ARRAY | TRUE |
### Object: `LayerOrStop`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|--------------|-----------------------------------------------------------|----------------|---------------|-----------|
| nameOrNumber | The name or number of the layer/stop | STRING | VARCHAR(64) | TRUE |
| description | A human readable description of the layer | STRING | VARCHAR(MAX) | TRUE |
| color | The color value that corresponds to the colorSystem defined| STRING | VARCHAR(64) | TRUE |
### Object: `Typeset`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|-------------------------------------------|----------------|---------------|-----------|
| sequenceNumber | The order of the typeset information | INT | INT | TRUE |
| value | The typeset to be used on the order | STRING | VARCHAR(1024) | TRUE |
| font | The font to use for the typeset | STRING | VARCHAR(64) | FALSE |
| fontSize | The font size to use for the typeset | DECIMAL | DECIMAL | FALSE |
### Object: `Shipment`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| customerPickup | The shipment will be a pickup and will not be shipped. | BOOLEAN | BOOLEAN | TRUE |
| [ShipTo](#object-shipto) | The object containing the ship to information | OBJECT | OBJECT | TRUE |
| [ThirdPartyAccount](#object-thirdpartyaccount) | The object containing the third party information for the shipping account to use and the business entity that is paying for the shipping. Known as ship using this account. | OBJECT | OBJECT | FALSE |
| shipReferences | Array of two strings max of identifiers used as the reference fields used during the shipping process. A shipReference can be a PO number, customer number, company name, etc. | STRING ARRAY | VARCHAR(64) | FALSE |
| packingListRequired | Packing list required | BOOLEAN | BOOLEAN | TRUE |
| blindShip | Require blind shipping | BOOLEAN | BOOLEAN | TRUE |
| allowConsolidation | Allow consolidation of shipments | BOOLEAN | BOOLEAN | TRUE |
| [FreightDetails](#object-freightdetails) | The details on the freight: carrier and service | OBJECT | OBJECT | FALSE |
| comments | Comments regarding the shipment for further clarification. Note: Use comments only when necessary, as it may cause delays in order processing. | STRING | VARCHAR(MAX) | FALSE |
### Object: `FreightDetails`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------|------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| carrier | The carrier name of the shipping vendor being requested (e.g. “UPS”, “FEDEX”, etc.) | STRING | VARCHAR(64) | FALSE |
| service | The service code of the shipping vendor for the service being requested (e.g. GROUND, 2DAY, NEXTDAY, etc.) | STRING | VARCHAR(64) | FALSE |
### Object: `ShipTo`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|------------------------------------------------------------------|----------------|---------------|-----------|
| shipmentId | The shipment id that can be referenced in the ShipmentLink object. | INT | INT | TRUE |
| [ContactDetails](#object-contactdetails) | The object containing the third party contact details | OBJECT | OBJECT | TRUE |
### Object: `ThirdPartyAccount`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|----------------|--------------------------------------------------------------------|----------------|---------------|-----------|
| accountName | The name of the account that will be used for shipping. | STRING | VARCHAR(64) | TRUE |
| accountNumber | The number of the account that will be used for shipping. | STRING | VARCHAR(64) | TRUE |
| [ContactDetails](#object-contactdetails) | The object containing the third party contact details | OBJECT | OBJECT | TRUE |
### Object: `Contact`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-----------------|---------------------------------------------------------------------------------------------------------------------------|---------------------|---------------|-----------|
| contactType | The type of contact. Values are enumerated: \{“Art”, “Bill”, “Expeditor”, “Order”, “Sales”, “Ship”, “Sold”\} | ENUMERATED STRING | VARCHAR(64) | TRUE |
| [ContactDetails](#object-contactdetails) | The object that contains the details about the contact. | OBJECT | OBJECT | TRUE |
| accountName | The name of the account that will be invoiced for the purchase order. Should be populated when the contactType is Bill. | STRING | VARCHAR(64) | FALSE |
| accountNumber | The number of the account that will be invoiced for the purchase order. Should be populated when the contactType is Bill. | STRING | VARCHAR(64) | FALSE |
### Object: `ContactDetails`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| attentionTo | Attention To (first and last name of contact) | STRING | VARCHAR(35) | FALSE |
| companyName | Company Name | STRING | VARCHAR(35) | FALSE |
| address1 | Address line 1 | STRING | VARCHAR(35) | FALSE |
| address2 | Address line 2 | STRING | VARCHAR(35) | FALSE |
| address3 | Address line 3 | STRING | VARCHAR(35) | FALSE |
| city | The city | STRING | VARCHAR(30) | FALSE |
| region | The 2 character US state abbreviation or 2-3 character non-US region. | STRING | VARCHAR(3) | FALSE |
| postalCode | The postal code | STRING | VARCHAR(10) | FALSE |
| country | The country in ISO 3166-2 format | STRING | VARCHAR(2) | FALSE |
| email | The Email | STRING | VARCHAR(128) | FALSE |
| phone | The phone number | STRING | VARCHAR(32) | FALSE |
| comments | Comments regarding the contact for further clarification. Note: Use comments only when absolutely necessary, as it may cause delays in order processing. | STRING | VARCHAR(MAX) | FALSE |
### Object: `SendPOResponse`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|---------------|-----------|
| transactionId | The transaction identifier in response indicating a successful transfer of a schematically valid PO payload. Details provided regarding further order processing and potential errors. | INT | INT | FALSE |
| [ServiceMessageArray](#object-contactdetails) | An array of ServiceMessage objects. | OBJECT | OBJECT | FALSE |
---
## Appendix A: Service Messages
### Object: `ServiceMessage`
| Field | Description | WSDL Data Type | SQL Data Type | Required? |
|-------------|------------------------------------------------------------------------------------------|------------------|---------------|-----------|
| code | The numerical value of the code | INT | INT | TRUE |
| description | Response for any message requiring notification to requestor | STRING | VARCHAR(256) | TRUE |
| severity | The severity of the message. Values are enumerated: \{Error, Information, Warning\} | ENUMERATED STRING| VARCHAR(64) | TRUE |
### Standardized Codes
Note: The range of `100-199` has been reserved for standardized error codes. The number `999` has been reserved for an error codes that is a “General Error - Contact System Service Provider”.
| Code | Description |
|------|---------------------------------------------------------------------------------------------------------------|
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 999 | General Error – Contact the System Service Provider Details: [Details] |
### Service Specific Code
Note: These error codes are only for this service.
| Code | Description |
|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 200 | Duplicated transaction detected. PO was not submitted. |
| 210 | Data violation: \{0\} (Occurs when a supplier does a pre-check on the data and finds that there is a mismatch of data and/or there are incorrect calculations) |
| 220 | Field is not supported: \{0\} |
| 230 | Field is partially supported: \{0\} (Use in situations where a supplier cannot accommodate different line level in-hands dates, fobs, etc. The responder will explain in the message what the course of action will be.) |
| 240 | The following is not supported by the Simple order type: \{0\} |
| 250 | The PO is received but there may be a delay in processing. |
## Diagrams
### SendPO Function
```mermaid
classDiagram
class SendPORequest {
+String wsVersion
+String id
+String password (optional)
+PO po
}
class SendPOResponse {
+int transactionId
+ServiceMessageArray serviceMessageArray
}
class PO {
+String orderType
+String orderNumber
+Date orderDate
+Date lastModified
+String orderVersion
+Double totalAmount
+String paymentTerms
+Boolean rush
+String currency
+DigitalProof digitalProof
+OrderContactArray orderContactArray
+ShipmentArray shipmentArray
+LineItemArray lineItemArray
+String termsAndConditions
+String salesChannel
+String promoCode
+TaxInformationArray taxInformationArray
}
class DigitalProof {
+Boolean required
+DigitalProofAddressesArray digitalProofAddressesArray
}
class OrderContactArray {
+Contact[] contacts
}
class ShipmentArray {
+Shipment[] shipments
}
class LineItemArray {
+LineItem[] lineItems
}
class TaxInformationArray {
+TaxInformation[] taxInformation
}
SendPORequest --|> SendPOResponse: requests
SendPORequest "1" -- "1" PO : contains
PO "1" -- "1" DigitalProof : contains
PO "1" -- "1" OrderContactArray : contains
PO "1" -- "1" ShipmentArray : contains
PO "1" -- "1" LineItemArray : contains
PO "1" -- "1" TaxInformationArray : contains
```
### Class Diagram for ServiceMessage and Additional Components
```mermaid
classDiagram
class ServiceMessage {
+Int code
+String description
+String severity
}
class Contact {
+String contactType
+ContactDetails contactDetails
}
class Shipment {
+Boolean customerPickup
+ShipTo shipTo
+ThirdPartyAccount thirdPartyAccount
+String[] shipReferences
+Boolean packingListRequired
+Boolean blindShip
+Boolean allowConsolidation
+FreightDetails freightDetails
}
class LineItem {
+String lineNumber
+String description
+String lineType
+Quantity quantity
+String fobId
+ToleranceDetails toleranceDetails
+Boolean allowPartialShipments
+Double unitPrice
+Double lineItemTotal
+Date requestedShipDate
+Date requestedInHands
+String referenceSalesQuote
+Program program
+String productId
+String customerProductId
+int lineItemGroupingId
+PartArray partArray
+Configuration configuration
}
class TaxInformation {
+String taxId
+String taxType
+Boolean taxExempt
+String[] taxJurisdiction
+Decimal taxAmount
}
class ContactDetails {
+String attentionTo
+String companyName
+String[] addressLines
+String city
+String region
+String postalCode
+String country
+String email
+String phone
+String comments
}
ServiceMessageArray --|> ServiceMessage: contains
OrderContactArray --|> Contact: contains
ShipmentArray
```
---
## PromoStandards Service Codes for Services
Source: https://docs.psrestful.com/standards/service-codes
# PromoStandards Service Codes
### The following table lists the service codes
| Service Code | Service Name |
| ------------ | ------------ |
| Product | Product Data |
| MED | Media Content|
| PPC | Product Pricing and Configuration |
| PDC | Product Compliance |
| INV | Inventory |
| PO | Purchase Order |
| ODRSTAT | Order Status |
| OSN | Order Shipment Notification|
| INVC | Invoice |
| SPCC | Service Provider Customer Credentials |
### Codes for PromoStandards Web Services in json format.
```json copy
[
{
"Code": "INV",
"Name": "Inventory"
},
{
"Code": "INVC",
"Name": "Invoice"
},
{
"Code": "MED",
"Name": "Media Content"
},
{
"Code": "OSN",
"Name": "Order Shipment Notification"
},
{
"Code": "ODRSTAT",
"Name": "Order Status"
},
{
"Code": "PDC",
"Name": "Product Compliance"
},
{
"Code": "Product",
"Name": "Product Data"
},
{
"Code": "PPC",
"Name": "Product Pricing and Configuration"
},
{
"Code": "PO",
"Name": "Purchase Order"
},
{
"Code": "SPCC",
"Name": "Service Provider Customer Credentials"
}
]
```
---
## Standardized Codes summarized per service
Source: https://docs.psrestful.com/standards/standardized-codes
# Standardized Codes summarized per service
## Core codes
The first group of codes are used in all services
The range of `100-199` has been reserved for standardized error codes.
The number 999 has been reserved for an error codes that is a "General Error - Contact System Service Provider"
| Code | Description |
| ---- | ------------------------------------------------------------------------------------- |
| 100 | ID (customerID) not found |
| 104 | This account is unauthorized to use this service. Please contact the service provider |
| 105 | Authentication Credentials failed |
| 110 | Authentication Credentials required |
| 115 | wsVersion not found |
| 120 | The following field(s) are required [Comma Delimited field names] |
| 125 | Not Supported |
| 130 | Function not supported |
| 999 | General Error – Contact the System Service Provider |
## Product 1.0.0 & 2.0.0
| Code | Description |
| ------ | ---------------------------- |
| 135 | Product color not found |
| 140 | Part Id not found |
| 145 | Part color not found |
| 150 | Part size not found |
## Media Content 1.1.0
| Code | Description |
|------|----------------------------------------------------------------------|
| 130 | Product Id not found |
| 135 | Product color not found |
| 140 | Part Id not found |
| 145 | Part color not found |
| 150 | Part size not found |
| 155 | Invalid Date Format |
| 160 | No Results Found |
## Product Price & Configuration 1.0.0
| Code | Description |
|------|-------------------------------------|
| 400 | productID not found |
| 401 | currencyID not found |
| 402 | priceType not found |
| 403 | fobId not found |
| 404 | localizationCountry not found |
| 405 | localizationLanguage not found |
| 406 | configurationType not found |
## Inventory 1.2.1 & 2.0.0
| Code | Description |
| ----- | ------------------------------------------------------------------------ |
| 600 | Product Id not found |
| 610 | Data violation: \{0\}
(Occurs when a supplier does a pre-check on the data and finds that there is a mismatch of data and/or there are incorrect calculations)|
| 620 | Field is not supported: \{0\} |
| 630 | Part Id not found |
## Purchase Order 1.0.0
| Code | Description |
|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 200 | Duplicated transaction detected. PO was not submitted. |
| 210 | Data violation: \{0\} (Occurs when a supplier does a pre-check on the data and finds that there is a mismatch of data and/or there are incorrect calculations) |
| 220 | Field is not supported: \{0\} |
| 230 | Field is partially supported: \{0\} (Use in situations where a supplier cannot accommodate different line level in-hands dates, fobs, etc. The responder will explain in the message what the course of action will be.) |
| 240 | The following is not supported by the Simple order type: \{0\} |
| 250 | The PO is received but there may be a delay in processing.
## Order Status 1.0.0 & 2.0.0
| Code | Description |
| ---- | ----------------------- |
| 200 | queryType not found |
| 210 | queryType not supported |
| 212 | Function does not support returning issues
[This message is used when the implementation does not support returning issues.] |
| 213 | returnIssueDetailType not supported
[This message is used when the implementation supports returning issues but not the type requested. For example, allIssues is specified but the function can only return openIssues. |
| 214 | Function does not support returning Product Details |
| 220 | No Orders were found for the requested criteria |
| 230 | No Issue was found for the given issue identifier |
| 240 | The specified Issue has already been resolved |
| 250 | statusTimeStamp is incorrect or is an invalid date range |
## Order Shipment Notification 1.0.0 & 2.0.0
| Code | Description |
| ---- | -------------------------------------------------------------- |
| 300 | queryType not found |
| 301 | referenceNumber not found |
| 302 | shipmentDateTimeStamp is incorrect or is an invalid date range |
## Product Compliance 1.0.0
| Code | Description |
| ---- | ----------------------------- |
| 600 | Product Id not found |
| 610 | Data violation: \{0\}
(Occurs when a supplier does a pre-check on the data and finds that there is a mismatch of data and/or there are incorrect calculations) |
| 620 | Field is not supported: \{0\} |
| 630 | Part Id not found |
---
## Content Encoding (Compression)
Source: https://docs.psrestful.com/compression
2. The API selects the best available method and compresses the response body
3. The response includes a `Content-Encoding` header indicating which method was used
4. Your HTTP client decompresses the response automatically
If no `Accept-Encoding` header is sent, the response is returned uncompressed.
## Supported Compression Methods
| Method | Header Value | Best For | Compatibility |
|--------|-------------|----------|---------------|
| Zstandard | `zstd` | Best overall performance and compression ratio | Modern clients (Chrome, Firefox, newer HTTP libraries) |
| Brotli | `br` | Excellent for text/JSON, smaller than gzip | Widely supported in modern clients |
| gzip | `gzip` | Universal fallback | All HTTP clients |
| deflate | `deflate` | Legacy support | All HTTP clients |
## Example Requests
### Requesting gzip compression
```bash copy
curl -s -D- -o /dev/null \
-H "X-API-Key: your-api-key" \
-H "Accept-Encoding: gzip" \
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
headers={
"X-API-Key": "your-api-key",
"Accept-Encoding": "gzip",
},
)
product = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
{
headers: {
"X-API-Key": "your-api-key",
"Accept-Encoding": "gzip",
},
}
);
const product = await response.json();
```
The response headers will include:
```
Content-Encoding: gzip
```
### Requesting Brotli compression
```bash copy
curl -s -D- -o /dev/null \
-H "X-API-Key: your-api-key" \
-H "Accept-Encoding: br" \
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
headers={
"X-API-Key": "your-api-key",
"Accept-Encoding": "br",
},
)
product = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
{
headers: {
"X-API-Key": "your-api-key",
"Accept-Encoding": "br",
},
}
);
const product = await response.json();
```
### Requesting multiple methods with priority
You can list multiple methods and let the server choose the best one:
```bash copy
curl -s -D- -o /dev/null \
-H "X-API-Key: your-api-key" \
-H "Accept-Encoding: zstd, br, gzip" \
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
headers={
"X-API-Key": "your-api-key",
"Accept-Encoding": "zstd, br, gzip",
},
)
product = response.json()
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
{
headers: {
"X-API-Key": "your-api-key",
"Accept-Encoding": "zstd, br, gzip",
},
}
);
const product = await response.json();
```
## Client Library Examples
Most HTTP libraries handle gzip compression automatically. Here's how to ensure compression is enabled or opt into newer methods.
```python copy
import requests
headers = {
"X-API-Key": "your-api-key",
"Accept-Encoding": "gzip, br",
}
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
headers=headers,
)
# requests automatically decompresses the response
product = response.json()
```
**Note:** The `requests` library sends `Accept-Encoding: gzip, deflate` by default. To use Brotli, install `brotli` or `brotlicffi` (`pip install brotli`), and `requests` will automatically include `br` in the header.
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
{
headers: {
"X-API-Key": "your-api-key",
},
}
);
// Browsers and modern runtimes handle decompression automatically
const product = await response.json();
```
**Note:** Browsers automatically negotiate compression and decompress responses. In Node.js 18+, the built-in `fetch` also handles this automatically.
```javascript copy
import axios from "axios";
const response = await axios.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/products/5989/",
{
headers: {
"X-API-Key": "your-api-key",
},
}
);
// axios decompresses gzip/deflate/br automatically
const product = response.data;
```
## Trade-offs
| Factor | Small Responses (< 1 KB) | Large Responses (> 10 KB) |
|--------|--------------------------|---------------------------|
| Bandwidth savings | Minimal | Significant (50–90% reduction) |
| Latency impact | Negligible | Noticeable improvement |
| CPU overhead | Not worth it | Well worth the trade-off |
Compression is most impactful for:
- **Product catalog imports** — full product data with descriptions, attributes, and part arrays
- **Pricing and configuration responses** — complex pricing structures with many charge lines
- **Media content listings** — media metadata across large product lines
For small responses like single inventory lookups or order status checks, the compression benefit is minimal.
## Best Practices
1. **Use gzip as your safe default.** It's universally supported and provides good compression for JSON responses.
2. **Upgrade to Brotli or Zstandard when possible.** If your HTTP client supports `br` or `zstd`, prefer them for better compression ratios and faster decompression.
3. **List multiple methods in `Accept-Encoding`.** Send `Accept-Encoding: zstd, br, gzip` and let the server select the best option. This ensures you get optimal compression without sacrificing compatibility.
4. **Focus on large payloads.** Compression has the most impact when importing catalogs or fetching product data. Don't worry about optimizing compression for transactional endpoints with small responses.
5. **Combine with caching.** Use compression together with [caching](/caching) for the fastest possible catalog imports — cached responses are returned immediately, and compression reduces transfer size.
## Related Documentation
- [Protocol Buffers](/protobuf) - Binary protobuf responses for smaller payloads and faster parsing
- [Caching](/caching) - API response caching and cache bypass
- [Rate Limits](/rate-limits) - API usage limits
---
## Product Life Cycle
Source: https://docs.psrestful.com/product-life-cycle
# Product Life Cycle
PromoStandards defines only two product states: **sellable** and **closeout**. This is insufficient for real-world product management — there's no way to represent products that are incomplete (missing images or pricing) or permanently discontinued.
PSRESTful extends this with a full **4-status life cycle**: `draft`, `active`, `closeout`, and `discontinued`.
---
## Status Definitions
| Status | Description | PromoStandards Equivalent |
|--------|-------------|--------------------------|
| `draft` | Product is incomplete — missing images (`primary_image_url` is empty) or has no pricing (`list_price` is 0 or null). Not ready to sell. | _(none)_ |
| `active` | Product is complete and available for sale. | Sellable |
| `closeout` | Product is being phased out, often at discounted prices. | Closeout |
| `discontinued` | Product is permanently removed from the catalog. No longer available for sale. | _(none)_ |
---
## Why We Added `draft`
When products are first ingested from suppliers, they may be incomplete. A product is automatically classified as `draft` when:
- `list_price` is `0` or `null`
- It has no primary image (`primary_image_url` is empty)
Draft products are **not ready for customer-facing catalogs**. This prevents showing products with $0 prices or missing images to end customers.
---
## Why We Added `discontinued`
`closeout` in PromoStandards implies temporary clearance — products being sold off at discounted prices. But there's no way to mark a product as **permanently gone**.
When a supplier stops making a product, distributors need to know so they can:
- Remove it from their storefronts
- Stop syncing inventory and pricing for it
- Avoid showing stale products to customers
`discontinued` fills this gap — it's a **terminal state** meaning "this product will not come back."
---
## How Status is Computed
Product status is determined by the following logic:
1. If `is_closeout` is `true` → **closeout**
2. If missing `primary_image_url` or `list_price` is `null`/`0` → **draft**
3. Otherwise → **active**
`discontinued` is typically set explicitly via admin action. However, it is also **auto-computed** in trivial cases: when a product's status is `closeout` and inventory is `0` across every part, the product is automatically marked as `discontinued`.
---
## Life Cycle Diagram
```
┌───────┐ ┌────────┐ ┌───────────┐ ┌──────────────┐
│ draft │──────▶│ active │──────▶│ closeout │──────▶│ discontinued │
└───────┘ └────────┘ └───────────┘ └──────────────┘
│ ▲
│ │
└────────────────────────────────────────┘
```
- **draft → active**: Product becomes complete (has pricing and images)
- **active → closeout**: Product is being phased out at discounted prices
- **active → discontinued**: Product is permanently removed from the catalog
- **closeout → discontinued**: Auto-computed when inventory reaches `0` across all parts
---
## Where Status Appears
### Extra APIs (v1 & v2)
The `status` field is included in all product responses from both `/extra/v1/products` and `/extra/v2/products`.
You can filter products by status using the query parameter:
```
GET /extra/v2/products?status=active
```
- **v1** uses `snake_case` field names
- **v2** uses `camelCase` field names
### Product Search in PromoSync
The search form includes a **"By Status"** dropdown filter (replacing the old "Is Closeout" boolean filter).
**Filter options:** Any, Active, Draft, Closeout, Discontinued
The default filter is **Active**.

Search results display **color-coded status badges**:
| Status | Badge Color |
|--------|-------------|
| Active | Green |
| Draft | Grey |
| Closeout | Yellow |
| Discontinued | Red |

### Product Detail in PromoSync
The product detail page displays the status badge next to the Product ID.

### Inventory & Pricing Sync in PromoSync
Products with `status == discontinued` (stored as the `psrestful.status` Shopify metafield) are automatically **skipped** during inventory and pricing sync updates.
This prevents wasting API calls and avoids updating data for products that will never be sold again.
---
## Backward Compatibility
The legacy `is_closeout` boolean field is maintained via **dual-write sync**:
| Status set to | `is_closeout` value |
|---------------|---------------------|
| `draft` | `false` |
| `active` | `false` |
| `closeout` | `true` |
| `discontinued` | `true` |
Existing integrations using `is_closeout` continue to work without modification.
---
## Related Documentation
- [Extra APIs](/extra-apis) — Full reference for the Extra APIs where the `status` field is available
- [How to Get Closeout Products](/guides/product-data/how-to-get-closeout-products) — Guide for retrieving closeout products
- [How to Get All Sellable Products](/guides/product-data/how-to-get-all-sellable-products) — Guide for retrieving sellable products
---
## Protocol Buffers
Source: https://docs.psrestful.com/protobuf
- **Polling inventory at high frequency** and want to minimize bandwidth and parse time
- **Building server-to-server pipelines** where human readability is not a concern
## How It Works
PSRESTful uses standard HTTP content negotiation:
1. Your client sends an `Accept: application/protobuf` header with the request
2. The API serializes the response into the binary protobuf format
3. The response is returned with a `Content-Type: application/protobuf` header
4. Your client deserializes the binary payload using the matching proto definition
If no `Accept` header is sent (or the value is `application/json`), the response is returned as JSON as usual.
## Supported Endpoints
Protobuf responses are only available on the read-only endpoints listed below. All other endpoints return JSON regardless of the `Accept` header.
| Service | Version | Endpoint Path | Response Message |
|---------|---------|---------------|------------------|
| Inventory | 1.2.1 | `/v1.2.1/suppliers/{id}/inventory/` | `inventory.v1.GetInventoryLevelsResponse` |
| Inventory | 2.0.0 | `/v2.0.0/suppliers/{id}/inventory/` | `inventory.v2.GetInventoryLevelsResponse` |
| Product Data | 1.0.0 | `/v1.0.0/suppliers/{id}/products/{productId}/` | `product.v1.GetProductResponse` |
| Product Data | 2.0.0 | `/v2.0.0/suppliers/{id}/products/{productId}/` | `product.v2.GetProductResponse` |
| Media Content | 1.0.0 | `/v1.0.0/suppliers/{id}/media/` | `media.v1.GetMediaContentResponse` |
| Media Content | 1.1.0 | `/v1.1.0/suppliers/{id}/media/` | `media.v1.GetMediaContentResponse` |
| PPC | 1.0.0 | `/v1.0.0/suppliers/{id}/ppc/decoration-colors/` | `ppc.v1.GetDecorationColorsResponse` |
| PPC | 1.0.0 | `/v1.0.0/suppliers/{id}/ppc/fob-points/` | `ppc.v1.GetFobPointsResponse` |
| PPC | 1.0.0 | `/v1.0.0/suppliers/{id}/ppc/available-locations/` | `ppc.v1.GetAvailableLocationsResponse` |
| PPC | 1.0.0 | `/v1.0.0/suppliers/{id}/ppc/available-charges/` | `ppc.v1.GetAvailableChargesResponse` |
| PPC | 1.0.0 | `/v1.0.0/suppliers/{id}/ppc/pricing-and-configuration/` | `ppc.v1.GetConfigurationAndPricingResponse` |
## JSON vs Protobuf Comparison
| Factor | JSON | Protobuf |
|--------|------|----------|
| Content type | `application/json` | `application/protobuf` |
| Human-readable | Yes | No (binary) |
| Payload size | Larger (text with field names) | Smaller (binary with field numbers) |
| Parse speed | Slower | Faster |
| Schema required | No | Yes (proto definitions) |
| Browser support | Native (`response.json()`) | Requires a protobuf library |
| Endpoint coverage | All endpoints | 11 read-only endpoints |
## Example Requests
```bash copy
curl -s \
-H "X-API-Key: your-api-key" \
-H "Accept: application/protobuf" \
-o response.bin \
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/?productId=5989"
```
```python copy
import requests
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/",
params={"productId": "5989"},
headers={
"X-API-Key": "your-api-key",
"Accept": "application/protobuf",
},
)
# response.content contains the raw binary protobuf payload
raw_bytes = response.content
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/?productId=5989",
{
headers: {
"X-API-Key": "your-api-key",
"Accept": "application/protobuf",
},
}
);
// response body is binary — read it as an ArrayBuffer
const buffer = await response.arrayBuffer();
```
The response headers will include:
```
Content-Type: application/protobuf
```
## Deserializing Responses
### Python — using `psdomain`
The `psdomain` package ships the compiled proto definitions for every supported service. Install it and use the generated message classes to parse the binary response.
```python copy
pip install psdomain
```
```python copy
from psdomain.proto.inventory.v2 import GetInventoryLevelsResponse
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/",
params={"productId": "5989"},
headers={
"X-API-Key": "your-api-key",
"Accept": "application/protobuf",
},
)
inventory = GetInventoryLevelsResponse()
inventory.ParseFromString(response.content)
for part in inventory.inventory.part_inventory_array:
print(f"{part.part_id}: {part.quantity_available.quantity.value}")
```
### JavaScript — using `protobufjs`
You can load the `.proto` definition files and decode the binary response with `protobufjs`.
```javascript copy
const root = await protobuf.load("inventory_v2.proto");
const ResponseType = root.lookupType(
"inventory.v2.GetInventoryLevelsResponse"
);
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/?productId=5989",
{
headers: {
"X-API-Key": "your-api-key",
"Accept": "application/protobuf",
},
}
);
const buffer = await response.arrayBuffer();
const inventory = ResponseType.decode(new Uint8Array(buffer));
for (const part of inventory.inventory.partInventoryArray) {
console.log(`${part.partId}: ${part.quantityAvailable.quantity.value}`);
}
```
## Combining with Compression
Protobuf responses can be compressed just like JSON responses. Send both headers together for the smallest possible payload:
```bash copy
curl -s \
-H "X-API-Key: your-api-key" \
-H "Accept: application/protobuf" \
-H "Accept-Encoding: zstd, br, gzip" \
-o response.bin \
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/?productId=5989"
```
```python copy
import requests
from psdomain.proto.inventory.v2 import GetInventoryLevelsResponse
response = requests.get(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/",
params={"productId": "5989"},
headers={
"X-API-Key": "your-api-key",
"Accept": "application/protobuf",
"Accept-Encoding": "zstd, br, gzip",
},
)
# requests decompresses automatically; content is the raw protobuf bytes
inventory = GetInventoryLevelsResponse()
inventory.ParseFromString(response.content)
```
```javascript copy
const response = await fetch(
"https://api.psrestful.com/v2.0.0/suppliers/HIT/inventory/?productId=5989",
{
headers: {
"X-API-Key": "your-api-key",
"Accept": "application/protobuf",
"Accept-Encoding": "zstd, br, gzip",
},
}
);
// Browser decompresses automatically; read the protobuf bytes
const buffer = await response.arrayBuffer();
```
See [Compression](/compression) for more details on supported encoding methods.
## Trade-offs
| Factor | JSON | Protobuf |
|--------|------|----------|
| Ease of use | Simple — parse with built-in methods | Requires proto definitions and a library |
| Debugging | Easy — readable in logs and browsers | Harder — binary requires tooling to inspect |
| Payload size | Larger | Smaller (typically 30–50% reduction) |
| Parse performance | Good | Faster (especially for large responses) |
| Endpoint support | All endpoints | 11 read-only endpoints |
## Best Practices
1. **Start with JSON, upgrade selectively.** Use JSON for development, testing, and low-volume calls. Switch to protobuf only for the specific endpoints where payload size or parse time is a bottleneck.
2. **Combine with compression.** Protobuf + gzip (or Brotli/Zstandard) gives you the smallest possible transfer size. See [Compression](/compression).
3. **Pin proto definitions to the API version.** The proto message schemas correspond to specific PSRESTful API versions. When you upgrade to a new API version, update your `psdomain` package to match.
4. **Check the `Content-Type` header for fallback.** If the server cannot produce a protobuf response (unsupported endpoint, server error), it will return JSON instead. Always check `Content-Type` before deserializing:
```python copy
if response.headers["Content-Type"] == "application/protobuf":
inventory = GetInventoryLevelsResponse()
inventory.ParseFromString(response.content)
else:
inventory = response.json()
```
## Related Documentation
- [Compression](/compression) — HTTP compression with gzip, Brotli, and Zstandard
- [Caching](/caching) — API response caching and cache bypass
- [Rate Limits](/rate-limits) — API usage limits
---
## Roles & Permissions
Source: https://docs.psrestful.com/roles
|---|---|
| **Admin** | Full access to every feature, including user management and supplier credentials |
| **Developer** | API key management plus all operations and product analytics features |
| **Operations** | View-only access to Orders, Shipments & Invoices |
| **Product Analyst** | View Best Sellers Reports, Product Search, and related analytics |
| **Customer Support** | Most restricted — basic Data Explorer only (Product Data, Media, and Inventory) |
## Permission Matrix
The table below shows exactly which dashboard features each role can access.
| Feature | Admin | Developer | Operations | Product Analyst | Customer Support |
|---|:---:|:---:|:---:|:---:|:---:|
| User Management | ✅ | — | — | — | — |
| Supplier Credentials | ✅ | — | — | — | — |
| Allowed Origins | ✅ | View only | — | — | — |
| Account Settings | ✅ | — | — | — | — |
| API Keys | ✅ | ✅ | — | — | — |
| Data Explorer (Advanced) | ✅ | ✅ | ✅ | ✅ | — |
| Data Explorer (Basic) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Orders, Shipments & Invoices | ✅ | ✅ | ✅ | — | — |
| Product Search & Best Sellers | ✅ | ✅ | — | ✅ | — |
| PPC | ✅ | ✅ | ✅ | ✅ | — |
| Price Calculator | ✅ | ✅ | ✅ | ✅ | — |
| Broken Links | ✅ | ✅ | ✅ | ✅ | — |
| User Profile | ✅ | ✅ | ✅ | ✅ | — |
## Role Details
### Admin
Admins have unrestricted access to every feature in the PSRESTful dashboard. This includes managing users, configuring supplier credentials, setting allowed origins, creating and revoking API keys, and accessing every data and reporting tool.
**Best for:** Account owners and team leads who need full control over the account.
### Developer
Developers can create and copy API keys, view allowed origins, and access all operational and product analytics features. They cannot manage users or supplier credentials.
**Best for:** Engineers who need to integrate with the PSRESTful API and also need visibility into order and product data.
### Operations
Operations users have view-only access to Orders, Shipments & Invoices. They can also use the advanced Data Explorer and access general features like PPC, Price Calculator, and Broken Links.
**Best for:** Fulfillment and logistics team members who need to monitor order status, shipment tracking, and invoices.
### Product Analyst
Product Analysts can view Best Sellers Reports, use Product Search, and access the advanced Data Explorer. They also have access to PPC, Price Calculator, and Broken Links.
**Best for:** Merchandising and product teams who need to analyze product data and trends.
### Customer Support
Customer Support is the most restricted role. These users can only access the **basic** Data Explorer, which allows them to look up Product Data, Media, and Inventory. They do not have access to the advanced Data Explorer, PPC, Price Calculator, Broken Links, or the User Profile page.
**Best for:** Support agents who need to look up product information to help end customers.
## Account Owner
The **Account Owner** is a special designation that is separate from roles. The owner is the person who originally created the PSRESTful account. Ownership grants the ability to manage billing and perform account-level actions that go beyond the Admin role. There is exactly one owner per account.
## How to Assign Roles
1. Log in to the [PSRESTful Dashboard](https://psrestful.com/dashboard/).
2. Navigate to **Account Settings** → **Users** tab.
3. Click **Invite User** and enter their email address.
4. Select a role from the dropdown.
5. The invited user will receive an email with instructions to join your account.
Only **Admins** (and the Account Owner) can invite users and assign roles.