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.

  1. Authenticate users through our OAuth-style flow
  2. Use access tokens to make requests to https://www.tone3000.com/api/v1/
  3. 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:

  1. Redirect users to the TONE3000 select page with your app details
  2. Users authenticate (via OTP email) and browse/select a tone
  3. 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 page
window.location.href = `https://www.tone3000.com/api/v1/select?app_id=${appId}&redirect_url=${redirectUrl}`;
  • Query parameters:
    NameTypeDescription
    app_idstringYour application or partner name (required)
    redirect_urlstringURL-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:

  1. Be prompted to login or create an account via OTP email code (if not already authenticated)
  2. See a streamlined interface for searching and viewing public tones, as well as their own private tones
  3. 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 data
const response = await fetch(toneUrl);
const tone = await response.json();
// The response is the tone object with models
console.log('Selected tone:', tone.title);
console.log('Models:', tone.models);
// Load the tone into your application
loadTone(tone);
}
  • Callback query parameters:
    NameTypeDescription
    tone_urlstringSecure URL to fetch the selected tone data (includes token and api_key parameters)
  • Response type: Tone & { models: Model[] }

Complete Example

Here's a complete example showing the entire Select integration flow:

// Step 1: Redirect to TONE3000 select page
function 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 page
async 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 data
const 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 models
console.log('Tone:', tone.title);
console.log('Description:', tone.description);
console.log('Models:', tone.models);
// Download the first model
if (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 file
const modelResponse = await fetch(model.model_url);
const modelBlob = await modelResponse.blob();
// Process the model file...
}
// Load tone into your application
loadToneIntoApp(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}`;
  • Search parameters:
    NameTypeDescription
    redirect_urlstringThe URL to redirect to after authentication.
    otp_onlybooleanIf 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 params
    const 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();

    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 expiration
    localStorage.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 refresh
    const 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 token
    const 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 expiration
    localStorage.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)));

    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 tokens
    localStorage.removeItem('tone3000_access_token');
    localStorage.removeItem('tone3000_refresh_token');
    localStorage.removeItem('tone3000_expires_at');
    // Redirect back to auth page to restart authentication
    const 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();

    Users

    Get a list of users with public content, sorted by various metrics.

    // Get top users by tones count
    const 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:
      NameTypeDescription
      sortUsersSortSort users by most stat (default: 'tones', optional)
      pagenumberPage number for pagination (default: 1, optional)
      page_sizenumberNumber of users per page (default: 10, max: 10, optional)
      querystringText 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:
      NameTypeDescription
      pagenumberPage number for pagination (default: 1, optional)
      page_sizenumberNumber 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:
      NameTypeDescription
      pagenumberPage number for pagination (default: 1, optional)
      page_sizenumberNumber 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:
      NameTypeDescription
      querystringSearch query term (optional, default: empty string)
      pagenumberPage number for pagination (default: 1, optional)
      page_sizenumberNumber of items per page (default: 10, max: 25, optional)
      sortTonesSortSort 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:
      NameTypeDescription
      tone_idnumberID of the tone to get models for
      pagenumberPage number for pagination (default: 1, optional)
      page_sizenumberNumber 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 browser
    const 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);
    };
    // Usage
    await 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 expires
    token_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 data
    const response = await t3kFetch('https://www.tone3000.com/api/v1/user');
    const userData = await response.json();