Authorization and OAuth Scopes
Learn how to use OAuth scopes to control access to Trading Card API resources with fine-grained permissions.
What Are OAuth Scopes?
OAuth scopes are permissions that define what actions an application can perform on behalf of a user. They implement the principle of least privilege by allowing you to request only the permissions your application actually needs.
When you authenticate with the Trading Card API using OAuth 2.0, you specify which scopes your application requires. The API then grants your access token permissions based on those scopes.
Why Scopes Matter
- Security - Limit the blast radius if a token is compromised
- User Trust - Users can see exactly what permissions they're granting
- Compliance - Meet security requirements for handling sensitive data
- Flexibility - Different clients (public apps, admin tools) need different permissions
Available Scopes
The Trading Card API supports five OAuth scopes for fine-grained access control:
Read Scopes (Data Access)
read:published
Access published cards, sets, and other resources only.
Use this scope for:
- Public-facing applications
- Read-only card browsers
- Public API integrations
- Applications that don't need access to draft content
Example use case: A public website displaying trading card information to visitors.
{
"scope": "read:published"
}
read:draft
Access draft and unpublished content in addition to published content.
Use this scope for:
- Content management systems
- Preview and staging environments
- Editorial workflows
- Applications that need to work with unpublished data
Example use case: A content management dashboard where editors prepare card data before publishing.
{
"scope": "read:draft"
}
read:all-status
Access all content regardless of publication status (published, draft, archived, etc.).
Use this scope for:
- Administrative tools
- Data migration applications
- Comprehensive reporting systems
- Applications needing full data visibility
Example use case: An admin dashboard showing all cards including drafts, published, and archived items.
{
"scope": "read:all-status"
}
read:published→ See published content onlyread:draft→ See published + draft contentread:all-status→ See all content (published, draft, archived, etc.)
Choose the minimum scope needed for your use case.
Write Scope
write
Create and update resources (POST, PATCH operations).
Allows:
- Creating new cards, sets, players, teams, etc.
- Updating existing resources
- Uploading card images
- Modifying metadata
Does NOT allow:
- Deleting resources (requires
deletescope) - Changing publication status (requires appropriate read scope)
Example use case: A collection management app where users add and edit their card collections.
{
"scope": "read:published write"
}
Delete Scope
delete
Delete resources (DELETE operations).
Allows:
- Soft deleting resources
- Permanently deleting resources
- Removing card images
Requires:
- Usually combined with read and write scopes for full CRUD operations
Example use case: An admin tool with full data management capabilities.
{
"scope": "read:all-status write delete"
}
The delete scope is powerful and should be requested only by trusted applications with administrative purposes. Most public applications should NOT request this scope.
Understanding Status-Based Access Control
One of the key features of the Trading Card API's scope system is status-based filtering. Different scopes automatically filter the data you can access based on the publication status of resources.
How It Works
| Scope | Published Cards | Draft Cards | Archived Cards |
|---|---|---|---|
read:published | ✅ Yes | ❌ No | ❌ No |
read:draft | ✅ Yes | ✅ Yes | ❌ No |
read:all-status | ✅ Yes | ✅ Yes | ✅ Yes |
This filtering happens automatically - you don't need to add status filters to your queries. The API respects your token's scopes and only returns resources you're permitted to see.
Example Scenarios
Public App (read:published):
// With read:published scope, this query returns only published cards
const cards = await client.cards.list({
'filter[set_id]': 'some-set-id'
});
// Result: Only published cards from the set
Admin Tool (read:all-status):
// With read:all-status scope, same query returns ALL cards
const cards = await client.cards.list({
'filter[set_id]': 'some-set-id'
});
// Result: Published, draft, AND archived cards from the set
Scope Combinations
Most real-world applications need multiple scopes. Here are common combinations:
Public Read-Only Application
{
"scope": "read:published"
}
Perfect for:
- Public card browsers
- Market price displays
- Collection showcases
- Public APIs
Collection Management App
{
"scope": "read:published write"
}
Perfect for:
- User collection tracking
- Personal collection apps
- Portfolio management
- Wishlist applications
Content Management System
{
"scope": "read:draft write"
}
Perfect for:
- Editorial workflows
- Content preparation
- Preview systems
- Staging environments
Administrative Dashboard
{
"scope": "read:all-status write delete"
}
Perfect for:
- Admin panels
- Data management tools
- Moderation systems
- Full CRUD applications
Data Migration Tool
{
"scope": "read:all-status write"
}
Perfect for:
- Importing data
- Syncing systems
- Bulk operations
- Data transformations
Requesting Scopes
When authenticating with OAuth 2.0, specify your required scopes in the token request:
OAuth 2.0 Client Credentials Flow
PHP
<?php
require_once 'vendor/autoload.php';
use TradingCardAPI\Client;
// Initialize client with specific scopes
$client = new Client([
'clientId' => getenv('TRADING_CARD_API_CLIENT_ID'),
'clientSecret' => getenv('TRADING_CARD_API_CLIENT_SECRET'),
'baseUrl' => 'https://api.tradingcardapi.com',
'scopes' => ['read:published', 'write'] // Request specific scopes
]);
// The client will automatically request these scopes when obtaining a token
$cards = $client->cards()->list();
JavaScript
const TradingCardAPI = require('trading-card-api');
// Initialize client with specific scopes
const client = new TradingCardAPI({
clientId: process.env.TRADING_CARD_API_CLIENT_ID,
clientSecret: process.env.TRADING_CARD_API_CLIENT_SECRET,
baseUrl: 'https://api.tradingcardapi.com',
scopes: ['read:published', 'write'] // Request specific scopes
});
// The client will automatically request these scopes when obtaining a token
const cards = await client.cards.list();
Python
import os
from trading_card_api import Client
# Initialize client with specific scopes
client = Client(
client_id=os.getenv('TRADING_CARD_API_CLIENT_ID'),
client_secret=os.getenv('TRADING_CARD_API_CLIENT_SECRET'),
base_url='https://api.tradingcardapi.com',
scopes=['read:published', 'write'] # Request specific scopes
)
# The client will automatically request these scopes when obtaining a token
cards = client.cards.list()
cURL (Manual Token Request)
#!/bin/bash
# Request token with specific scopes
curl -X POST "https://api.tradingcardapi.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=read:published write"
# Response includes access_token with the requested scopes
# {
# "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
# "token_type": "Bearer",
# "expires_in": 3600,
# "scope": "read:published write"
# }
Personal Access Tokens (PAT)
When generating a Personal Access Token, you can select which scopes to grant:
- Log in to your Trading Card API account
- Navigate to Settings → API → Personal Access Tokens
- Click Generate New Token
- Select scopes from the available options:
- ☐ read:published
- ☐ read:draft
- ☐ read:all-status
- ☐ write
- ☐ delete
- Click Generate Token
Select only the scopes you actually need. You can always generate a new token with different scopes if your requirements change.
Security Best Practices
1. Request Minimum Required Scopes
Only request the scopes your application actually needs:
// ❌ Bad: Requesting all scopes "just in case"
const client = new TradingCardAPI({
scopes: ['read:all-status', 'write', 'delete']
});
// ✅ Good: Request only what you need
const client = new TradingCardAPI({
scopes: ['read:published'] // Read-only public app
});
2. Use Different Tokens for Different Operations
Create separate tokens/clients with different scopes for different parts of your application:
// Public-facing client (read-only)
const publicClient = new TradingCardAPI({
scopes: ['read:published']
});
// Admin client (full access)
const adminClient = new TradingCardAPI({
scopes: ['read:all-status', 'write', 'delete']
});
// Use the appropriate client for each operation
app.get('/api/cards', async (req, res) => {
const cards = await publicClient.cards.list(); // Use read-only client
res.json(cards);
});
app.delete('/api/cards/:id', async (req, res) => {
await adminClient.cards.delete(req.params.id); // Use admin client
res.sendStatus(204);
});
3. Store Tokens Securely
- Never commit tokens to version control
- Use environment variables or secure secret management
- Rotate tokens regularly
- Revoke unused tokens
# .env file
TRADING_CARD_API_PUBLIC_TOKEN=token_with_read_published
TRADING_CARD_API_ADMIN_TOKEN=token_with_all_scopes
# .gitignore
.env
4. Handle Scope Errors Gracefully
Always check for and handle insufficient scope errors:
try {
await client.cards.delete(cardId);
} catch (error) {
if (error.status === 403 && error.code === 'insufficient_scope') {
console.error('Token lacks delete scope');
// Show user-friendly message or redirect to upgrade
}
throw error;
}
5. Audit Scope Usage
Regularly review which scopes your tokens have and whether they're still needed:
// Check what scopes your token has
const tokenInfo = await client.auth.introspect();
console.log('Active scopes:', tokenInfo.scope);
// If scopes are too broad, generate a new token with narrower scopes
Choosing the Right Scopes
Use this decision tree to select appropriate scopes:
Do you need to modify data?
No → Use read-only scopes
Yes → Add write scope
Do you need to delete data?
No → Don't request delete
Yes → Add delete scope (admin tools only)
What content do you need to access?
Only published content → read:published
Published + draft content → read:draft
All content (including archived) → read:all-status
Common Use Cases
| Application Type | Recommended Scopes |
|---|---|
| Public card browser | read:published |
| User collection tracker | read:published write |
| Content management system | read:draft write |
| Admin dashboard | read:all-status write delete |
| Data import tool | read:all-status write |
| Analytics/reporting | read:all-status |
| Public API integration | read:published |
Troubleshooting Scope Issues
Insufficient Scope Error
Error Response:
{
"errors": [{
"status": "403",
"code": "insufficient_scope",
"title": "Insufficient Scope",
"detail": "The access token does not have the required scope for this operation",
"meta": {
"required_scope": "write",
"current_scopes": ["read:published"]
}
}]
}
Solutions:
- Generate a new token with the required scope
- Request additional scopes when authenticating
- Use a different token that has the necessary permissions
Not Seeing Expected Data
If queries return fewer results than expected, check your scope:
// With read:published scope
const cards = await client.cards.list();
// Returns: 100 published cards
// With read:all-status scope
const cards = await client.cards.list();
// Returns: 150 cards (100 published + 30 draft + 20 archived)
Solution: Ensure your token has the appropriate read scope for the data you need.
Scope Not Recognized
Error: invalid_scope when requesting a token
Common causes:
- Typo in scope name (e.g.,
read_publishedinstead ofread:published) - Requesting deprecated or non-existent scopes
- Requesting scopes not available to your account tier
Solution: Verify scope names match exactly: read:published, read:draft, read:all-status, write, delete
Migrating from Simple Scopes
If you're upgrading from an earlier version that used simple read and write scopes:
Scope Migration
| Old Scope | New Equivalent | Notes |
|---|---|---|
read | read:published | Access published content only |
write | write | No change |
delete | delete | No change |
Update Your Code
// Old approach (deprecated)
const client = new TradingCardAPI({
scopes: ['read', 'write']
});
// New approach (recommended)
const client = new TradingCardAPI({
scopes: ['read:published', 'write'] // Be explicit about read level
});
Benefits of Granular Read Scopes
The new read:published, read:draft, and read:all-status scopes provide:
- Better security - Request only the publication statuses you need
- Clearer permissions - Explicit about what content you can access
- Flexible access control - Different apps can have different visibility levels
Next Steps
Learn More
- Authentication Guide - Complete authentication setup
- API Endpoints - See which scopes each endpoint requires
- Security Best Practices - Secure your API integration
Try It Out
- Generate a Personal Access Token with specific scopes
- Make API requests and observe status-based filtering
- Try requesting data with different scope combinations
- Implement proper error handling for insufficient scope errors
Need Help?
- Support - Get help with scope configuration
- API Status - Check service status
- Community - Connect with other developers
Summary
OAuth scopes provide fine-grained access control for the Trading Card API:
✅ Five scopes - read:published, read:draft, read:all-status, write, delete
✅ Status-based filtering - Automatic filtering based on read scope level
✅ Principle of least privilege - Request only what you need
✅ Flexible combinations - Mix scopes for your use case
✅ Security - Separate tokens for different operations
Choose your scopes carefully, request the minimum permissions needed, and handle scope errors gracefully to build secure, reliable applications.