Introduction
Welcome to the TONE3000 API. This RESTful API provides programmatic access to TONE3000 accounts, tones and models. To use the API, you or your users will need to authenticate via their TONE3000 account.
Note: This is version 1 of our API. Endpoints and data structures may change as we improve the platform.
Integration Options
TONE3000 offers two ways to integrate: a low-code Select flow for quick integrations, or full API access for complete control.
Select
The fastest way to integrate TONE3000 into your application. An OAuth-like flow that handles authentication and tone browsing through our interface.
- No need to build authentication or browsing UI
- Users authenticate and select tones through TONE3000's interface
- Your app receives complete tone data with downloadable models
- Perfect for plugins, native apps, and quick integrations
Full API Access
For applications that need complete programmatic control over the user experience.
- Authenticate users through our OAuth-style flow
- Use access tokens to make requests to https://www.tone3000.com/api/v1/
- Query user profile, users, created tones, favorited tones, search tones, and model data
Rate Limit
100 requests per minute by default. For production applications please email support@tone3000.com.
Support & Feedback
Questions, issues, or feedback? Contact support@tone3000.com - we'd love to hear from you.
Select
Select is a low-code API integration option that uses an OAuth-like flow to allow your application's end-users to select and load tones through the TONE3000 interface. This flow handles authentication and tone browsing automatically, making integration simple and fast.
Integration Flow
The Select flow consists of three simple steps:
- Redirect users to the TONE3000 select page with your app details
- Users authenticate (via OTP email) and browse/select a tone
- TONE3000 redirects back to your app with a secure tone URL
Step 1: Redirect to Select Page
Redirect (or for native apps, launch an in-app web browser) users to the TONE3000 select page. Include your application name and the URL where users should be redirected after selecting a tone.
Note for Native Apps: For iOS, Android, or other native applications, launch the select URL in an in-app browser (e.g., SFSafariViewController on iOS, Chrome Custom Tabs on Android). This provides a secure, familiar experience while keeping users within your app. Make sure to use a deep link URL (e.g., yourapp://callback) as your redirect_url so the callback returns to your app.
const appId = 'your-awesome-app';const redirectUrl = encodeURIComponent('https://your-app.com/callback');// Redirect user to TONE3000 select pagewindow.location.href = `https://www.tone3000.com/api/v1/select?app_id=${appId}&redirect_url=${redirectUrl}`;
- Query parameters:
Name Type Description app_idstring Your application or partner name (required) redirect_urlstring URL-encoded callback URL where users will be redirected after selecting a tone (required)
Step 2: User Authentication & Selection
On the TONE3000 select page, users will:
- Be prompted to login or create an account via OTP email code (if not already authenticated)
- See a streamlined interface for searching and viewing public tones, as well as their own private tones
- Select a tone by clicking on it
Once a user selects a tone, TONE3000 will redirect them back to your redirect_url with a special tone_url query parameter.
Step 3: Handle Callback & Fetch Tone Data
After the user selects a tone, TONE3000 redirects back to your redirect_url with a tone_url query parameter. This URL is a secure endpoint that requires both a secret token and your user's API key to access.
The tone_url endpoint returns the complete tone object directly, including the models array with pre-signed downloadable URLs. Use this to load the selected tone into your application.
Note for Native Apps: For iOS, Android, or other native applications, use a deep link URL as your redirect_url (e.g., yourapp://callback). Configure your app to handle this deep link scheme, and parse the tone_url parameter from the deep link URL when your app is launched. Then make an HTTP request from your native code to fetch the tone data from the tone_url endpoint.
// On your callback page (e.g., https://your-app.com/callback)const urlParams = new URLSearchParams(window.location.search);const toneUrl = urlParams.get('tone_url');if (toneUrl) {// Fetch the tone dataconst response = await fetch(toneUrl);const tone = await response.json();// The response is the tone object with modelsconsole.log('Selected tone:', tone.title);console.log('Models:', tone.models);// Load the tone into your applicationloadTone(tone);}
Complete Example
Here's a complete example showing the entire Select integration flow:
// Step 1: Redirect to TONE3000 select pagefunction startToneSelection() {const appId = 'your-awesome-app';const redirectUrl = encodeURIComponent('https://your-app.com/callback');window.location.href = `https://www.tone3000.com/api/v1/select?app_id=${appId}&redirect_url=${redirectUrl}`;}// Step 3: Handle callback on your callback pageasync function handleCallback() {const urlParams = new URLSearchParams(window.location.search);const toneUrl = urlParams.get('tone_url');if (!toneUrl) {console.error('No tone_url parameter found');return;}try {// Fetch the tone dataconst response = await fetch(toneUrl);if (!response.ok) {throw new Error(`Failed to fetch tone: ${response.statusText}`);}const tone = await response.json();// Now you have the complete tone with modelsconsole.log('Tone:', tone.title);console.log('Description:', tone.description);console.log('Models:', tone.models);// Download the first modelif (tone.models && tone.models.length > 0) {const model = tone.models[0];console.log(`Model: ${model.name} (${model.size})`);console.log(`Download URL: ${model.model_url}`);// Download the model fileconst modelResponse = await fetch(model.model_url);const modelBlob = await modelResponse.blob();// Process the model file...}// Load tone into your applicationloadToneIntoApp(tone);} catch (error) {console.error('Error loading tone:', error);}}
Benefits
- Low-code integration: No need to build authentication or tone browsing UI
- OAuth-like flow: Familiar pattern that users trust
- Secure: Tone URLs include secret tokens and require valid API keys
- Complete data: Returns full tone metadata with all models and download URLs
- Works everywhere: Perfect for web apps, native apps with in-app browsers, and more
Authentication
Initial Setup
1. Redirect users to the TONE3000 authentication page
Start the authentication flow by redirecting users to the TONE3000 authentication page. Make sure to encode your application's URL as the redirect_url parameter.
Note: If your application only supports OTP authentication (e.g. IOT devices), include otp_only=true in the search parameters. This will send the user a 6-digit OTP code instead of a magic link.
import { APP_URL } from '@/config/constants';const redirectUrl = encodeURIComponent(APP_URL);window.location.href = `https://www.tone3000.com/api/v1/auth?redirect_url=${redirectUrl}`;
| Name | Type | Description |
|---|---|---|
redirect_url | string | The URL to redirect to after authentication. |
otp_only | boolean | If true, the user will be sent a 6-digit OTP code instead of a magic link. |
2. Exchange API Key for Session Tokens
After successful authentication, TONE3000 will redirect back to your application's redirect_url with an api_key parameter. You'll need to exchange this API key for session tokens. The response will include access and refresh tokens, along with expiration information.
// Get API key from URL search paramsconst urlParams = new URLSearchParams(window.location.search);const apiKey = urlParams.get('api_key');const response = await fetch('https://www.tone3000.com/api/v1/auth/session', {method: 'POST',body: JSON.stringify({ api_key: apiKey })});const data = await response.json();
- Response type: Session
Session Management
1. Store Session Tokens
After exchanging the API key, you'll receive an access token and refresh token. Store these tokens securely in your application (e.g., in localStorage for web apps). Also store the expiration time to enable proactive token refresh.
// Store tokens and expirationlocalStorage.setItem('tone3000_access_token', data.access_token);localStorage.setItem('tone3000_refresh_token', data.refresh_token);localStorage.setItem('tone3000_expires_at', String(Date.now() + (data.expires_in * 1000)));
2. Making Authenticated Requests
Include the access token in the Authorization header for all API requests. The token should be prefixed with "Bearer ". Before making a request, check if the token is about to expire and refresh it if needed.
// Check if token needs refreshconst expiresAt = parseInt(localStorage.getItem('tone3000_expires_at') || '0');if (Date.now() > expiresAt) {await refreshTokens();}const response = await fetch(url, {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});
3. Token Refresh Flow
When an API request returns 401 (Unauthorized) or when proactively refreshing, use the refresh token to get a new access token. The response will include new tokens and expiration information.
// Refresh the access tokenconst refreshResponse = await fetch('https://www.tone3000.com/api/v1/auth/session/refresh', {method: 'POST',body: JSON.stringify({refresh_token: refreshToken,access_token: accessToken})});const newTokens = await refreshResponse.json();// Store the new tokens and expirationlocalStorage.setItem('tone3000_access_token', newTokens.access_token);localStorage.setItem('tone3000_refresh_token', newTokens.refresh_token);localStorage.setItem('tone3000_expires_at', String(Date.now() + (newTokens.expires_in * 1000)));
- Response type: Session
4. Handling Refresh Failure
If token refresh fails (e.g., refresh token is expired), clear the stored tokens and redirect the user back to the authentication page to restart the authentication flow.
// Clear stored tokenslocalStorage.removeItem('tone3000_access_token');localStorage.removeItem('tone3000_refresh_token');localStorage.removeItem('tone3000_expires_at');// Redirect back to auth page to restart authenticationconst redirectUrl = encodeURIComponent(window.location.href);window.location.href = `https://www.tone3000.com/api/v1/auth?redirect_url=${redirectUrl}`;
User
Get information about the currently authenticated user.
const response = await fetch('https://www.tone3000.com/api/v1/user', {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const user = await response.json();
- Response type: User
Users
Get a list of users with public content, sorted by various metrics.
// Get top users by tones countconst response = await fetch('https://www.tone3000.com/api/v1/users?sort=tones&page=1&page_size=10', {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const result = await response.json();// Search for users with username containing "john"const searchResponse = await fetch('https://www.tone3000.com/api/v1/users?query=john', {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const searchResult = await searchResponse.json();
- Response type: PaginatedResponse<PublicUser[]>
- Query parameters:
Name Type Description sortUsersSort Sort users by most stat (default: 'tones', optional) pagenumber Page number for pagination (default: 1, optional) page_sizenumber Number of users per page (default: 10, max: 10, optional) querystring Text search query to filter users by username (optional)
Tones
Created Tones
Get a list of tones created by the currently authenticated user.
const response = await fetch(`https://www.tone3000.com/api/v1/tones/created?page=${page}&page_size=${pageSize}`, {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const tones = await response.json();
- Response type: PaginatedResponse<Tones[]>
- Query parameters:
Name Type Description pagenumber Page number for pagination (default: 1, optional) page_sizenumber Number of items per page (default: 10, max: 100, optional)
Favorited Tones
Get a list of tones favorited by the currently authenticated user.
const response = await fetch(`https://www.tone3000.com/api/v1/tones/favorited?page=${page}&page_size=${pageSize}`, {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const tones = await response.json();
- Response type: PaginatedResponse<Tones[]>
- Query parameters:
Name Type Description pagenumber Page number for pagination (default: 1, optional) page_sizenumber Number of items per page (default: 10, max: 100, optional)
Search Tones
Search and filter tones with various filters and sorting options.
const response = await fetch(`https://www.tone3000.com/api/v1/tones/search?query=${query}&page=${page}&page_size=${pageSize}&sort=${sort}&gear=${gear}&sizes=${sizes}`, {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const tones = await response.json();
- Response type: PaginatedResponse<Tones[]>
- Query parameters:
Name Type Description querystring Search query term (optional, default: empty string) pagenumber Page number for pagination (default: 1, optional) page_sizenumber Number of items per page (default: 10, max: 25, optional) sortTonesSort Sort order (default: 'best-match' if query provided, 'trending' otherwise) gearGear[] Filter by gear type. Can be comma-separated for multiple values (optional) sizesSize[] Filter by model sizes. Can be comma-separated for multiple values (optional)
Models
Get a list of models for a specific tone accessible by the current authenticated user.
const response = await fetch(`https://www.tone3000.com/api/v1/models?tone_id=${toneId}&page=${page}&page_size=${pageSize}`, {headers: {'Authorization': `Bearer ${accessToken}`,'Content-Type': 'application/json'}});const models = await response.json();
- Response type: PaginatedResponse<Model[]>
- Query parameters:
Name Type Description tone_idnumber ID of the tone to get models for pagenumber Page number for pagination (default: 1, optional) page_sizenumber Number of items per page (default: 10, max: 100, optional)
The model_url field can be used to download the model. It is only valid for tones that are accessible by the current authenticated user.
// Download model in browserconst downloadModel = async (modelUrl: string) => {const response = await fetch(modelUrl, {headers: {'Authorization': `Bearer ${accessToken}`}});if (!response.ok) {throw new Error('Failed to download model');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = modelUrl.split('/').pop() || 'model';document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);};// Usageawait downloadModel(model.model_url);
Enums
Gear
enum Gear {Amp = 'amp',FullRig = 'full-rig',Pedal = 'pedal',Outboard = 'outboard',Ir = 'ir'}
Platforms
enum Platform {Nam = 'nam',Ir = 'ir',AidaX = 'aida-x',AaSnapshot = 'aa-snapshot',Proteus = 'proteus'}
Licenses
enum License {T3k = 't3k',CcBy = 'cc-by',CcBySa = 'cc-by-sa',CcByNc = 'cc-by-nc',CcByNcSa = 'cc-by-nc-sa',CcByNd = 'cc-by-nd',CcByNcNd = 'cc-by-nc-nd',Cco = 'cco'}
Sizes
enum Size {Standard = 'standard',Lite = 'lite',Feather = 'feather',Nano = 'nano',Custom = 'custom'}
UsersSort
enum UsersSort {Tones = 'tones',Downloads = 'downloads',Favorites = 'favorites',Models = 'models'}
TonesSort
enum TonesSort {BestMatch = 'best-match',Newest = 'newest',Oldest = 'oldest',Trending = 'trending',DownloadsAllTime = 'downloads-all-time'}
Types
Session
The response type for both session creation and refresh endpoints.
interface Session {access_token: str;refresh_token: str;expires_in: number; // seconds until token expirestoken_type: 'bearer';}
EmbeddedUser
interface EmbeddedUser {id: int;username: str;avatar_url: str | null;url: str;}
User
interface User extends EmbeddedUser {bio: str | null;links: str[] | null;created_at: str;updated_at: str;}
PublicUser
Public user information with content counts, returned by the users endpoint.
interface PublicUser {id: int;username: str;bio: str | null;links: str[] | null;avatar_url: str | null;downloads_count: number;favorites_count: number;models_count: number;tones_count: number;url: str;}
Make
interface Make {id: number;name: str;}
Tag
interface Tag {id: number;name: str;}
Paginated Response
interface PaginatedResponse<T> {data: T[];page: number;page_size: number;total: number;total_pages: number;}
Tone
interface Tone {id: number;user_id: int;user: EmbeddedUser;created_at: str;updated_at: str;title: str;description: str | null;gear: Gear;images: str[] | null;is_public: boolean | null;links: str[] | null;platform: Platform;license: License;sizes: Size[];makes: Make[];tags: Tag[];models_count: number;downloads_count: number;favorites_count: number;url: str;}
Model
interface Model {id: number;created_at: str;updated_at: str;user_id: int;model_url: str;name: str;size: Size;tone_id: number;}
Example
For a complete end-to-end example of how to use the TONE3000 API in a React application, check out our example repository. This repository demonstrates:
- User authentication and session management
- Fetching and displaying user data
- Working with tones and models
- Handling API responses and errors
- Token refresh flow implementation
The example app uses the t3kFetch utility to handle authentication and token management automatically.
// Example: Fetching user dataconst response = await t3kFetch('https://www.tone3000.com/api/v1/user');const userData = await response.json();