UltraCart REST API Errors - Developer's Reference Guide
Table of Contents
Overview
The UltraCart REST API uses standard HTTP status codes and returns structured error messages to help developers diagnose and resolve issues. This guide covers both the legacy REST API (Version 1) and the current REST API (Version 2).
API Base URLs:
-
Version 2:
https://secure.ultracart.com/rest/v2 -
Version 1 (Legacy):
https://secure.ultracart.com/rest
Important Note: Version 1 is legacy and maintained only for backward compatibility. All new development should use Version 2 with the official SDKs.
Error Response Structure
Version 2 Error Format
All API responses in Version 2 include an error object when errors occur:
{
"error": {
"developer_message": "Detailed technical error message for developers",
"user_message": "User-friendly error message suitable for display"
},
"metadata": {}
}
Key Fields:
-
developer_message: Technical details for debugging (log this for troubleshooting) -
user_message: Customer-facing message (safe to display to end users) -
metadata: Additional context about the error (optional)
Example Error Handling (Multiple Languages)
PHP:
$api_response = $order_api->getOrder($order_id, $expansion);
if ($api_response->getError() != null) {
error_log($api_response->getError()->getDeveloperMessage());
error_log($api_response->getError()->getUserMessage());
exit();
}
Python:
api_response = order_api.get_order(order_id, expand=expand)
if api_response.error:
print(f"Developer Message: {api_response.error.developer_message}")
print(f"User Message: {api_response.error.user_message}")
exit()
JavaScript/TypeScript:
const apiResponse = await orderApi.getOrder({orderId, expand: expansion});
if (apiResponse.error) {
console.error('Developer Message:', apiResponse.error.developer_message);
console.error('User Message:', apiResponse.error.user_message);
throw new Error('Failed to retrieve order');
}
C#:
OrderResponse apiResponse = orderApi.GetOrder(orderId, expansion);
if (apiResponse.Error != null) {
Console.Error.WriteLine(apiResponse.Error.DeveloperMessage);
Console.Error.WriteLine(apiResponse.Error.UserMessage);
Environment.Exit(1);
}
Ruby:
api_response = order_api.get_orders_batch(
order_query_batch: order_batch,
opts: { '_expand' => expansion }
)
if api_response.error
warn "Developer Message: #{api_response.error.developer_message}"
warn "User Message: #{api_response.error.user_message}"
exit 1
end
Version 1 Error Format
Version 1 returns errors as an array in the errors field:
{
"errors": [
"Error message 1",
"Error message 2"
]
}
Validation Errors:
{
"validationErrors": [
{
"field": "email",
"message": "Email address is required"
}
]
}
HTTP Status Codes
2xx Success Codes
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request succeeded, response contains data |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded, no content in response (e.g., delete operations) |
4xx Client Error Codes
| Code | Status | Description | Common Causes |
|---|---|---|---|
| 400 | Bad Request | Malformed request syntax or invalid parameters | Invalid JSON, missing required fields, incorrect data types |
| 401 | Unauthorized | Authentication required or failed | Missing/invalid API key, expired OAuth token, insufficient permissions |
| 403 | Forbidden | Valid request but server refuses to respond | Permission denied, API access not enabled for user, IP restriction |
| 404 | Not Found | Resource does not exist | Invalid order ID, item ID, or endpoint URL; typos in URL |
| 405 | Method Not Allowed | HTTP method not supported for endpoint | Using GET instead of POST, or vice versa |
| 422 | Unprocessable Entity | Request well-formed but contains semantic errors | Invalid field values, failed business logic validation |
| Endpoint Type | Rate Limit | Notes |
|---|---|---|
| Most endpoints | Varies by endpoint | Standard rate limiting |
Item inventory (GET /items/inventory) | Max 1 call per 15 minutes | Strictly enforced |
| Batch operations | 500 items max per request | Exceeding returns 400 error |
SDK Auto-Retry (PHP): The PHP SDK automatically retries when rate limits are hit:
// PHP SDK handles retry automatically when rate limit triggered
// Other SDKs to follow in future updates
Manual Retry Logic:
JavaScript:
async function retryRequest(requestFunc, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await requestFunc();
if (response.status !== 429) {
return response;
}
// Get retry-after header if present
const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
throw new Error('Max retries exceeded');
}
Python:
import time
import random
def retry_request(request_func, max_retries=5):
for attempt in range(max_retries):
try:
response = request_func()
if response.status_code != 429:
return response
retry_after = response.headers.get('Retry-After')
delay = int(retry_after) if retry_after else (2 ** attempt)
time.sleep(delay + random.uniform(0, 1))
except Exception as e:
if attempt == max_retries - 1:
raise e
raise Exception("Max retries exceeded")
Best Practices:
-
Implement exponential backoff with jitter
-
Honor
Retry-Afterheader when present -
Cache frequently accessed data (items, allowed countries)
-
Batch requests when possible
-
Monitor API usage patterns
Cart and Checkout Errors
Missing Checkout Redirect
Common Mistake: Not redirecting to redirectToUrl after checkout.
Error: Order never completes, payment not processed.
Explanation: The /rest/cart/checkout endpoint is NOT the final step. After calling checkout successfully (no errors in response), you must redirect the browser to the URL found in checkoutResponse.redirectToUrl.
Correct Flow:
jQuery.ajax({
url: '/rest/cart/checkout',
type: 'POST',
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(checkoutRequest),
dataType: 'json'
}).done(function(checkoutResponse) {
if (!checkoutResponse.errors || checkoutResponse.errors.length === 0) {
// CRITICAL: Must redirect to complete order
window.location.href = checkoutResponse.redirectToUrl;
} else {
// Display errors to user
displayErrors(checkoutResponse.errors);
}
});
What happens during redirect:
-
Payment gateway processing
-
Fraud screening
-
Upsell gauntlet (if configured)
-
Order finalization
-
Receipt generation
If errors occur during redirect phase:
-
User is redirected back to
checkoutRequest.redirectOnErrorUrl -
This is typically your checkout page
-
Error details are in URL parameters or session
Preorder Item Errors Overriding Other Errors
Issue: When cart contains preorder items, preorder warnings may hide validation errors.
Solution: Separate cart updates from checkout calls:
// Step 1: Update cart with items
jQuery.ajax({
url: '/rest/cart',
type: 'PUT',
data: JSON.stringify(cart),
// ...
}).done(function(updatedCart) {
// Step 2: Then call checkout
jQuery.ajax({
url: '/rest/cart/checkout',
type: 'POST',
data: JSON.stringify(checkoutRequest),
// ...
});
});
Hosted Fields Required (PCI 3.0)
Error Context: As of June 2015, credit card numbers cannot be sent directly to the API.
Solution: Use UltraCart Hosted Fields for PCI 3.0 compliance.
Old (No longer works):
cart.creditCardNumber = '4111111111111111'; // ❌ Not allowed
Correct (Hosted Fields):
// 1. Load Hosted Fields library
<script src="https://token.ultracart.com/hosted_fields/..."></script>
// 2. Initialize Hosted Fields
UltraCart.HostedFields.initialize({
// configuration
});
// 3. Tokenize card before checkout
UltraCart.HostedFields.tokenize(function(token) {
cart.creditCardToken = token; // ✅ Use token instead
// Proceed with checkout
});
Cart Session Expiration
Symptom: Cart ID becomes invalid, causing 404 errors.
Cause: Cart sessions expire after inactivity.
Solution:
-
Store
cartIdin browser cookie -
Create new cart if existing cart returns 404
-
Implement cart recovery for logged-in customers
function getOrCreateCart() {
const savedCartId = getCookie('UltraCartCartId');
if (savedCartId) {
// Try to retrieve existing cart
return fetchCart(savedCartId).catch(error => {
if (error.status === 404) {
// Cart expired, create new one
return createNewCart();
}
throw error;
});
}
return createNewCart();
}
Arbitrary Unit Cost Errors
Error: Using arbitraryUnitCost on items not configured for it.
Cause: Item must be configured in UltraCart admin to allow arbitrary pricing.
Solution:
-
Configure item in admin: Items → Edit Item → Pricing
-
Enable "Allow Arbitrary Unit Cost"
-
Set min/max bounds if needed
// Now this will work
{
"item_id": "DONATION",
"quantity": 1,
"arbitraryUnitCost": 50.00
}
Distribution Center Not Configured for REST API
HTTP Status: 400
Headers:
HTTP/1.1 400
X-UltraCart-Request-Id: 8376DD8AC3EA6A019A4ADA5E291F43489
Content-Type: application/json; charset=UTF-8
UC-REST-ERROR: Distribution center is not configured for REST API transport.
Error Response:
{
"error": {
"developer_message": "Distribution center is not configured for REST API transport.",
"user_message": "Distribution center is not configured for REST API transport."
},
"metadata": {}
}
Cause: The distribution center is not properly configured to use REST API as its transmission mechanism.
Solution:
Step 1: Navigate to Distribution Center Settings
-
Log into secure.ultracart.com
-
Go to: Configuration → Checkout → Shipping → Distribution Centers
- Direct URL:
https://secure.ultracart.com/merchant/configuration/shipping/distributionCenterListLoad.do
- Direct URL:
-
Click Edit next to the distribution center
Step 2: Verify Basic Configuration In the Distribution Center tab, ensure these required fields are filled:
-
Code
-
Name
-
Postal Code
-
State
-
Country
Step 3: Configure Transmission Mechanism
-
Click on the Transmission Mechanism tab
-
Set Transmission Method to: REST
-
In the Authorized Application (required) dropdown:
-
Select your authorized OAuth application
-
This links your API application to the distribution center
-
-
Important: Scroll to bottom and click Save
Step 4: Verify Configuration After saving, the distribution center should now accept REST API calls for:
-
Order creation via REST
-
Order status updates
-
Shipping notifications
-
Inventory management
Common Mistakes:
-
Forgetting to save after changing transmission method
-
Not having an authorized OAuth application created yet
-
Selecting wrong application from dropdown
-
Missing required fields in Distribution Center tab (prevents saving)
Create Authorized Application (if needed):
-
Navigate to: Configuration → OAuth Applications
-
Create new OAuth application
-
Grant necessary permissions (orders, shipping, etc.)
-
Return to Distribution Center settings
-
Select newly created application from dropdown
Verification Test:
// Test if distribution center is properly configured
// Attempt to create or update an order via REST API
// Should succeed without the 400 error
Related Errors:
-
If you haven't created an OAuth application yet, the "Authorized Application" dropdown will be empty
-
If the distribution center basic info is incomplete, you won't be able to save the transmission mechanism settings
Payment Processing Errors
These errors occur during the redirect phase after /rest/cart/checkout:
Credit Card Declined
User Message: "Your card was declined. Please use a different payment method."
Common Reasons:
-
Insufficient funds
-
Incorrect billing address (AVS mismatch)
-
Card expired
-
Card reported lost/stolen
-
Incorrect CVV
User Action: User is redirected back to redirectOnErrorUrl to try again.
Payment Gateway Timeout
Symptom: Order processing takes unusually long, then fails.
Cause: Payment gateway not responding.
UltraCart Handling:
-
Timeout handled during redirect phase
-
User redirected back to retry
-
No browser timeout because processing happens server-side
CVV Validation Failure
Error: CVV verification failed.
Notes:
-
UltraCart does not store CVV values
-
If using stored credit cards, configure gateway to not require CVV
-
For new cards, CVV is always required unless gateway configured otherwise
Resource Errors
404 Not Found - Resource Does Not Exist
Common Causes:
- Invalid Order ID:
// Error: Order doesn't exist
orderApi.getOrder('INVALID-ORDER-ID', expansion)
- Invalid Item ID:
// Error: Item not found
itemApi.getItem('NON_EXISTENT_ITEM')
- Typo in endpoint URL:
// Error: Wrong endpoint
'/rest/v2/order/DEMO-123' // ❌ Missing 's'
'/rest/v2/orders/DEMO-123' // ✅ Correct
- Trailing slash causes 404:
// Error: Trailing slash
'/rest/v2/orders/' // ❌
'/rest/v2/orders' // ✅
Solution: Verify:
-
Resource identifier is correct
-
Endpoint URL matches API documentation
-
No trailing slashes
-
Proper URL encoding of parameters
404 for Private Resources (Security Measure)
Note: UltraCart returns 404 (not 403) for private resources you don't have access to, avoiding confirmation of their existence.
If you get 404 for a resource you know exists:
-
Check authentication credentials
-
Verify API key has required scopes/permissions
-
Confirm user has necessary role (e.g., organization owner)
Server Errors
500 Internal Server Error
Cause: Unexpected server-side error.
Action:
-
Check request for malformed data
-
Retry request after brief delay
-
If persists, contact UltraCart support with request details
-
Provide request ID if available
502 Bad Gateway / 503 Service Unavailable
Cause: Server temporarily unavailable or under maintenance.
Action:
-
Wait and retry after exponential delay
-
Check UltraCart status page
-
If prolonged, contact support
Example Retry Logic:
async function robustRequest(requestFunc) {
const delays = [1000, 2000, 5000, 10000]; // milliseconds
for (let delay of delays) {
try {
const response = await requestFunc();
if (response.status < 500) {
return response;
}
} catch (error) {
// Log error
}
await new Promise(resolve => setTimeout(resolve, delay));
}
throw new Error('Service unavailable after retries');
}
Troubleshooting Tips
Debugging Checklist
-
Check browser console (for frontend issues)
-
Open developer tools (F12)
-
Look for JavaScript errors
-
Inspect network tab for API calls
-
Check request/response headers and payloads
-
-
Examine response headers
-
Error messages often in response headers
-
Look for
X-UC-Erroror similar headers
-
-
Enable debug mode (SDK-specific)
// PHP - Enable debug mode
$client = new GuzzleHttp\Client(['verify' => true, 'debug' => true]);
-
Use Server-Side Logging
-
Enable in UltraCart admin: Developer Tools → Call History Log
-
Navigate to: Configuration → Manage Users → Edit User
-
Grant "API Access" permission
-
View last 100 API calls with full details
-
-
Check API call history
-
Log into secure.ultracart.com
-
Go to Developer Tools → Call History Log
-
View request/response details
-
Examine transmission logs for errors
-
Common Pitfalls
-
Not handling errors in response objects
-
Always check for
errororerrorsfield -
Don't assume success based on HTTP 200 status
-
-
Forgetting merchant ID
-
Error: "Missing Merchant ID"
-
Solution: Always provide via header, query param, cookie, or StoreFront URL
-
-
Skipping checkout redirect
-
Critical: Must redirect to
redirectToUrl -
Order will not complete without this step
-
-
Using deprecated API version
-
Version 1 is legacy
-
Use Version 2 with SDKs for all new development
-
-
Incorrect content-type header
// Wrong
headers: { 'Content-Type': 'text/plain' }
// Correct
headers: { 'Content-Type': 'application/json; charset=UTF-8' }
- Not URL encoding parameters
// Wrong
`/rest/v2/item/${itemId}` // If itemId contains special chars
// Correct
`/rest/v2/item/${encodeURIComponent(itemId)}`
Getting Help
Community Support:
-
Post issues on GitHub: https://github.com/UltraCart
-
Check existing issues for solutions
-
Response time: 24-48 hours
Professional Services:
-
Rate: $100/hour (1 hour minimum)
-
For API development questions and troubleshooting
-
Contact via UltraCart admin panel
What to Include in Support Requests:
-
Full error message (developer_message and user_message)
-
Request details (endpoint, method, parameters)
-
Response body and headers
-
Steps to reproduce
-
API call history log entry (from UltraCart admin)
-
SDK version and language
Best Practices
Error Handling
- Always check for errors first:
if (apiResponse.error) {
// Handle error
return;
}
// Process success response
- Log errors appropriately:
// Log technical details server-side
console.error('API Error:', apiResponse.error.developer_message);
// Show user-friendly message to customer
displayMessage(apiResponse.error.user_message);
- Implement retry logic with backoff:
-
Use exponential backoff for retries
-
Add jitter to prevent thundering herd
-
Respect
Retry-Afterheader
- Handle errors gracefully:
try {
const result = await apiCall();
return result;
} catch (error) {
if (error.status === 429) {
// Rate limited - retry later
await retryWithBackoff();
} else if (error.status >= 500) {
// Server error - show maintenance message
showMaintenanceMessage();
} else {
// Client error - show specific message
showErrorMessage(error.message);
}
}
Security
- Never expose API keys in client-side code:
// ❌ WRONG - API key in browser
const API_KEY = 'your-api-key-here';
// ✅ CORRECT - API key on server only
// Use OAuth for browser-based apps
- Use OAuth for third-party integrations:
-
Simple API Key for internal/server-side use
-
OAuth 2.0 for apps that access customer data
- Restrict API users:
-
Create dedicated API user
-
Grant only "API Access" permission
-
Restrict by IP address if possible
- Use HTTPS always:
-
All API endpoints require HTTPS
-
Never send credentials over HTTP
Performance
- Use expansion parameters wisely:
// Only request fields you need
expansion = "items,summary" // ✅ Minimal payload
// Avoid requesting everything
expansion = "*" // ❌ Largest payload
- Implement caching:
// Cache rarely-changing data
const allowedCountries = await cache.get('countries') ||
await fetchAndCache('countries');
- Batch operations when possible:
// Batch order retrieval
orderApi.getOrdersBatch({
order_ids: ['ORDER1', 'ORDER2', 'ORDER3']
});
- Monitor API usage:
-
Track request counts
-
Set up alerts near rate limits
-
Optimize inefficient code patterns
Development Workflow
-
Use demo merchant ID for testing:
-
Merchant ID:
DEMO -
Test without affecting production
-
-
Test in sandbox before production:
-
Validate integrations thoroughly
-
Test error scenarios
-
Verify payment processing
-
-
Keep SDKs updated:
# Check for updates regularly
composer update ultracart/rest_api_v2_sdk_php
pip install --upgrade ultracart-rest-api-v2
npm update ultracart_rest_api_v2_typescript
- Follow SDK conventions:
-
Use language-specific SDK patterns
-
Leverage built-in retry logic (PHP SDK)
-
Follow SDK documentation for your language
Version History
-
Version 2 (Current) - REST API with OpenAPI specs and SDKs
-
Released: 2015+
-
Languages: PHP, Python, Java, C#, JavaScript/TypeScript, Ruby
-
Features: Comprehensive error handling, retry logic, OAuth support
-
-
Version 1 (Legacy) - Original REST API
-
Status: Maintained but no new features
-
Use: Only for legacy implementations
-
Migration: All new projects should use Version 2
-
Quick Reference
Most Common Errors
| Error | HTTP Code | Quick Fix |
|---|---|---|
| Missing Merchant ID | 400 | Add X-UC-Merchant-Id header |
| Permission Denied | 401 | Enable API Access for user |
| Rate Limited | 429 | Wait and retry with backoff |
| Order Not Found | 404 | Verify order ID is correct |
| Invalid JSON | 400 | Validate JSON syntax |
| Distribution Center Not Configured | 400 | Set transmission method to REST, select authorized app |
| Credit Card Declined | N/A | User must try different card |
Essential Headers
// Minimum required headers
{
'X-UC-Merchant-Id': 'YOUR_MERCHANT_ID',
'Content-Type': 'application/json; charset=UTF-8',
'cache-control': 'no-cache'
}
// For Simple API Key authentication
{
'x-ultracart-simple-key': 'YOUR_API_KEY'
}
// For OAuth authentication
{
'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
}
Key Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/rest/v2/checkout/cart | GET | Retrieve cart |
/rest/v2/checkout/cart | PUT | Update cart |
/rest/v2/checkout/cart/checkout | POST | Submit checkout |
/rest/v2/checkout/cart/validate | POST | Validate cart |
/rest/v2/order/orders/{order_id} | GET | Get order details |
/rest/v2/item/items/{item_id} | GET | Get item details |
Additional Resources
-
Main API Documentation: https://www.ultracart.com/api/
-
GitHub Repositories: https://github.com/UltraCart
-
Confluence Documentation: https://ultracart.atlassian.net/wiki/
-
SDK Samples: https://github.com/UltraCart/sdk_samples
-
Postman Collection: Available from main API docs
-
OpenAPI Spec: JSON Schema available in API docs
FAQ
Q: Why are my REST API requests blocked (403 error) when using Claude Code or another AI coding tool?
A: UltraCart may block REST API requests when the application sends a generic Python user agent, such as the default user agent generated by some scripts, libraries, or AI coding tools. UltraCart’s firewall does not allow requests that appear to come from generic Python scripts because they can resemble automated scraping or abusive traffic.
To avoid this issue, configure your REST API application to send a more descriptive application user agent. For example:
OpenAPI-Generator/4.1.91/python
When using tools such as Claude Code, review the generated HTTP client code and confirm that the User-Agent header is set explicitly. The user agent should identify the application or SDK rather than relying on a default Python value.
Example header:
User-Agent: OpenAPI-Generator/4.1.91/python
Tip: If your API requests suddenly receive firewall-related errors or appear to be blocked before reaching the UltraCart REST API, check the
User-Agentheader first. A generic Python user agent is a common cause when the client code was generated or modified by an AI coding tool.
Support
Free Support:
-
GitHub Issues: Post technical questions
-
Documentation: Comprehensive guides and examples
-
Community: Developer forums
Paid Support:
-
Professional Services: $100/hour
-
Custom integration assistance
-
Priority troubleshooting
Contact:
-
Email: support@ultracart.com
-
Portal: secure.ultracart.com
-
Phone: Available in admin panel
Last Updated: November 2025
API Version: 2.0
Guide Version: 1.0