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

All integration options use OAuth 2.0 with PKCE and issue the same access token, giving you full access to tone data, model files, and user libraries. Choose the flow that fits your product:

Select

Your users browse and pick a tone directly within TONE3000's interface. Zero auth UI or tone browser to build. Ideal for plugins, DAW integrations, and native apps.

Load Tone

Your app specifies a tone_id; TONE3000 authenticates the user and verifies access. If the tone is unavailable, the user can browse for a replacement. Ideal for applications where the TONE3000 tone ID is known in advance.

Full API Access

Authenticate via OAuth and use the access token to build any experience: custom tone browsers, library sync, in-app model management.

Explore all integration options in the example app repository.

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.

Terms

Use of the TONE3000 API is governed by our API Terms of Service, Design Requirements, and Commercial Terms. Please review and follow these requirements as you explore the API.

Authentication

API Keys

Generate your keys in settings. Your account has two types of keys:

Publishable Key

client_id

Identifies your application in OAuth flows. Safe to include in client-side code, mobile apps, and browser environments. Used as the client_id parameter on every authorization and token request. The publishable key is your durable application identifier; it is preserved across secret revocation and regeneration so embedded devices using it keep working.

Secret Key

t3k_cs_…

Server-only Bearer credential. Treat it like a database password: never embed it in client-side code, mobile binaries, or any environment that reaches a user's device. Pass it as Authorization: Bearer t3k_cs_… on direct API calls. You can revoke or regenerate the secret in settings at any time without affecting OAuth flows or your publishable key. To fully retire an integration (taking the publishable key offline), contact support.

OAuth Authorization Flow

Use the OAuth flow when your integration is user-facing. Your app redirects the user to TONE3000, they authorize and complete the flow, and TONE3000 redirects back with an authorization code. The prompt parameter controls which flow runs:

  • prompt omitted: standard authorization only
  • prompt=select_tone: user browses and selects a tone (see Select)
  • prompt=load_tone: verifies access to a specific tone_id (see Load Tone)

1. Redirect the user to TONE3000

Generate a PKCE code_verifier and derive the code_challenge (SHA-256, base64url-encoded). Store the code_verifier and state; you'll need them to complete the token exchange.

GET https://www.tone3000.com/api/v1/oauth/authorize

TONE3000 responds with a redirect, so there is no response body to read. If the user isn't signed in, they'll be prompted to log in first. Once authenticated, TONE3000 completes the flow and redirects back to your redirect_uri.

ParameterRequiredDescription
client_idYesYour publishable key
redirect_uriYesWhere to return the user after the flow. If you've registered redirect URIs in settings, only those will be accepted
response_typeYesMust be code
code_challengeYesBase64url-encoded SHA-256 hash of your code_verifier
code_challenge_methodYesMust be S256
stateYesA random value you generate; returned in the callback to verify the response is legitimate
login_hintNoOptional. Hints which email address to pre-fill on the login screen. Malformed or overlong values are ignored (the flow still continues).
promptNoControls the flow type. See values above. Omit for standard authorization.
tone_idConditionalRequired when prompt=load_tone
gearsNoRestrict catalog by gear type. Applies to select_tone and load_tone flows. Separate multiple values with _ (e.g. amp_amp-cab). full-rig and ir are deprecated alias values; see Deprecated Fields. See also Gear.
formatNoRestrict catalog to a specific model format. Applies to select_tone and load_tone flows. platform is a deprecated alias; see Deprecated Fields. See also Format.
architectureNoFilter by model architecture. Accepts 1 (A1), 2 (A2), or custom. Omitting falls back to A1 + Custom (legacy default; excludes A2). Applies to select_tone and load_tone flows.
calibratedNoPass true to lock the catalog to tones that have at least one calibrated model. Applies to select_tone and load_tone flows.
menubarNoSet to true to show a navigation bar at the top of the TONE3000 experience with back, forward, refresh, and close buttons. Applies to every flow, including the sign-in page shown when prompt is omitted. Recommended for in-app browsers and popup windows.
previewNoPass true to render audition players in the flow, so users can hear a tone before selecting it. Players appear on the search results, creator profiles, and the tone detail page. Playback is demo-only (no live input), and requires a browser or webview with SharedArrayBuffer — where that's unavailable the players are omitted and the rest of the flow is unaffected. Applies to the select_tone flow.
const codeVerifier = crypto.randomUUID().replace(/-/g, '') + crypto.randomUUID().replace(/-/g, '');
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
const state = crypto.randomUUID();
sessionStorage.setItem('t3k_code_verifier', codeVerifier);
sessionStorage.setItem('t3k_state', state);
const params = new URLSearchParams({
client_id: 'YOUR_PUBLISHABLE_KEY',
redirect_uri: 'https://your-app.com/callback',
response_type: 'code',
code_challenge: codeChallenge,
code_challenge_method: 'S256',
state,
// login_hint: 'user@example.com', // optional: pre-fill login form (OpenID / OAuth)
// prompt: 'select_tone' | 'load_tone'
// architecture: '2', // optional: 1 | 2 | 'custom'. Omit for legacy A1 + Custom. Applies to select_tone and load_tone.
// calibrated: 'true', // optional: lock to tones with a calibrated model. Applies to select_tone and load_tone.
// menubar: 'true', // optional: show navigation bar with back/forward/refresh/close
// preview: 'true', // optional: audition players in the flow (demo-only playback). Applies to select_tone.
});
window.location.href = `https://www.tone3000.com/api/v1/oauth/authorize?${params}`;

2. Handle the callback

After the user completes the flow, TONE3000 redirects to your redirect_uri. Always verify state before proceeding; this protects against CSRF attacks.

Callback parameters

ParameterDescription
codeShort-lived authorization code to exchange for tokens
stateThe value you sent in Step 1; verify this matches before proceeding
tone_idPresent on success for prompt=select_tone and prompt=load_tone flows
errorPresent on failure
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
const error = params.get('error');
if (state !== sessionStorage.getItem('t3k_state')) {
throw new Error('State mismatch. Possible CSRF attack.');
}
if (error) {
// Handle error, e.g. error === 'access_denied'
return;
}

Token Exchange

Exchange the authorization code for an access token. Include the access token as a Bearer token on every subsequent API request.

POST https://www.tone3000.com/api/v1/oauth/token

Content-Type: application/x-www-form-urlencoded

Request body

FieldDescription
grant_typeMust be authorization_code
codeThe authorization code from the callback
code_verifierThe PKCE verifier you generated in Step 1
redirect_uriMust match the redirect_uri used in Step 1
client_idYour publishable key
const response = await fetch('https://www.tone3000.com/api/v1/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
code_verifier: sessionStorage.getItem('t3k_code_verifier')!,
redirect_uri: 'https://your-app.com/callback',
client_id: 'YOUR_PUBLISHABLE_KEY',
}),
});
const { access_token, refresh_token, expires_in } = await response.json();

Response

FieldTypeDescription
access_tokenstringBearer token for API requests
refresh_tokenstringUse to get a new access token without re-authorization
token_typestringAlways bearer
expires_innumberSeconds until the access token expires
scopestringThe scope granted, as passed in the authorization request

Session Management

1. Store tokens

Store the access token, refresh token, and expiration time securely.

sessionStorage.setItem('t3k_access_token', access_token);
sessionStorage.setItem('t3k_refresh_token', refresh_token);
sessionStorage.setItem('t3k_expires_at', String(Date.now() + expires_in * 1000));

2. Make authenticated requests

Include the access token as a Bearer token in the Authorization header. Check expiration before each request and refresh proactively.

const expiresAt = parseInt(sessionStorage.getItem('t3k_expires_at') || '0');
if (Date.now() > expiresAt) await refreshTokens();
const response = await fetch('https://www.tone3000.com/api/v1/user', {
headers: { Authorization: `Bearer ${sessionStorage.getItem('t3k_access_token')}` },
});

3. Refresh the access token

When the access token expires, POST to the same token endpoint with grant_type=refresh_token.

Request body

FieldDescription
grant_typeMust be refresh_token
refresh_tokenThe refresh token from the previous token response
client_idYour publishable key
const response = await fetch('https://www.tone3000.com/api/v1/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: sessionStorage.getItem('t3k_refresh_token')!,
client_id: 'YOUR_PUBLISHABLE_KEY',
}),
});
const { access_token, refresh_token, expires_in } = await response.json();
sessionStorage.setItem('t3k_access_token', access_token);
sessionStorage.setItem('t3k_refresh_token', refresh_token);
sessionStorage.setItem('t3k_expires_at', String(Date.now() + expires_in * 1000));

The response shape is the same as the initial token exchange.

4. Handle refresh failure

A 400 response with error: invalid_grant means the refresh token has expired. Clear stored tokens and restart the authorization flow. Refresh tokens are long-lived, but handle this gracefully so users aren't left in a broken state.

sessionStorage.removeItem('t3k_access_token');
sessionStorage.removeItem('t3k_refresh_token');
sessionStorage.removeItem('t3k_expires_at');
startAuthorization(); // restart from Step 1

Select

Select lets your users browse the TONE3000 catalog and pick a tone without you building any auth UI or tone browser. The user is redirected to TONE3000, signs in, browses, and selects a tone, then lands back in your app with an access token and the selected tone_id ready to use.

Step 1: Send the User to TONE3000

Build the authorization URL with prompt=select_tone. You can optionally restrict the catalog by gear type or format so users only see tones relevant to your product. See Authentication for PKCE generation details.

Native apps: Use a deep link (e.g. yourapp://callback) as your redirect_uri and open the authorization URL in an in-app browser (SFSafariViewController on iOS, Chrome Custom Tabs on Android).

// PKCE generation: see Authentication section
const params = new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
redirect_uri: 'https://your-app.com/callback',
response_type: 'code',
code_challenge: codeChallenge,
code_challenge_method: 'S256',
state,
prompt: 'select_tone',
// gears: 'amp-cab', // optional: amp-cab, amp, cab, pedal, outboard, space, experimental
// gears: 'amp_amp-cab', // optional: multiple values separated by _
// format: 'nam', // optional: nam, aida-x, aa-snapshot, proteus, ir
// architecture: '2', // optional: 1 | 2 | 'custom'. Omit for legacy A1 + Custom.
// menubar: 'true', // optional: show navigation bar with back/forward/refresh/close
// preview: 'true', // optional: audition players in the flow (demo-only playback)
// login_hint: 'user@example.com', // optional: pre-fill sign-in email (see Authentication)
});
window.location.href = `https://www.tone3000.com/api/v1/oauth/authorize?${params}`;
ParameterDescription
promptMust be select_tone (required)
gearsRestrict the catalog to one or more gear types. Separate multiple values with _ (e.g. amp_amp-cab). The gear filter will be locked to your selection. full-rig and ir are deprecated alias values; see Deprecated Fields (optional, see also Gear)
formatRestrict the catalog to a specific model format. Only compatible tones will be shown. platform is a deprecated alias; see Deprecated Fields (optional, see also Format)
architectureFilter the catalog by model architecture. Accepts 1 (A1), 2 (A2), or custom. Only tones with at least one model in the selected architecture are shown. Omitting returns the legacy A1 + Custom set (excludes A2) so API users that pre-date A2 don't receive models they can't load. (optional)
menubarSet to true to show a navigation bar at the top of the TONE3000 experience with back, forward, refresh, and close buttons. Recommended for in-app browsers and popup windows (optional)
previewSet to true to render audition players in the flow so users can hear a tone before selecting it. Players appear on the search results, creator profiles, and the tone detail page. Playback is demo-only — there is no live-input mode, since that isn't available inside an embedded webview. Requires a browser or webview that supports SharedArrayBuffer; where it's unavailable the players are omitted and the rest of the flow works normally (optional)
login_hintOptional. Pre-fills the email field if the user must sign in. Same rules as Authentication; malformed or overlong values are ignored.

Step 2: The User Browses and Selects

TONE3000 handles sign-in, browsing, and selection. The user sees the full public catalog and their own private tones, filtered by any gear or format constraints you specified. Once they tap a tone, TONE3000 redirects them back to your app.

Step 3: Handle the Callback

TONE3000 redirects to your redirect_uri with a code, state, and the tone_id the user selected. Verify state before proceeding.

If the menubar is enabled and the user clicks the close button, the redirect will include canceled=true instead of a tone_id. If the user had already signed in, a code is still included and can be exchanged for tokens. If the user closed before signing in, no code is present.

const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
const toneId = params.get('tone_id');
const canceled = params.get('canceled') === 'true';
if (state !== sessionStorage.getItem('t3k_state')) {
throw new Error('State mismatch. Possible CSRF attack.');
}
if (canceled) {
// User exited without selecting a tone.
// If code is present, you can still exchange it for tokens.
// If code is absent, the user closed before signing in.
return;
}

Step 4: Exchange the Code and Fetch the Tone

Exchange the authorization code for an access token (see Token Exchange), then use the tone_id from the callback to fetch tone metadata and model download URLs.

// Token exchange: see Authentication section
const { access_token } = await exchangeCode(code);
// Fetch tone metadata
const tone = await fetch(`https://www.tone3000.com/api/v1/tones/${toneId}`, {
headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json());
// Fetch models, each has a model_url for downloading
const { data: models } = await fetch(
`https://www.tone3000.com/api/v1/models?tone_id=${toneId}`,
{ headers: { Authorization: `Bearer ${access_token}` } },
).then(r => r.json());

Load Tone

Load Tone is for apps that already know which tone they want. Your app passes a tone_id and TONE3000 handles authentication and access verification. If the tone is accessible, the user is redirected straight back to your app. If it's private or deleted, the user can browse for a replacement, and your app receives the result either way. You can optionally pass gears and format filters to scope the replacement browse view.

Step 1: Send the User to TONE3000

Build the authorization URL with prompt=load_tone and the tone_id you want to load. Optionally pass gears and format to filter the replacement browse view if the tone is inaccessible. See Authentication for PKCE generation details.

// PKCE generation: see Authentication section
const params = new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
redirect_uri: 'https://your-app.com/callback',
response_type: 'code',
code_challenge: codeChallenge,
code_challenge_method: 'S256',
state,
prompt: 'load_tone',
tone_id: '42',
// gears: 'amp', // optional: filter replacement browse by gear type
// format: 'nam', // optional: filter replacement browse by format
// architecture: '2', // optional: 1 | 2 | 'custom' (replacement browse). Omit for legacy A1 + Custom.
// menubar: 'true', // optional: show navigation bar with back/forward/refresh/close
// login_hint: 'user@example.com', // optional: pre-fill sign-in email (see Authentication)
});
window.location.href = `https://www.tone3000.com/api/v1/oauth/authorize?${params}`;
ParameterDescription
promptMust be load_tone (required)
tone_idThe ID of the tone to load (required)
gearsRestrict the replacement browse view to one or more gear types. Separate multiple values with _ (e.g. amp_amp-cab). Only applied when the user needs to browse for a replacement. full-rig and ir are deprecated alias values; see Deprecated Fields (optional, see also Gear)
formatRestrict the replacement browse view to a specific model format. Only applied when the user needs to browse for a replacement. platform is a deprecated alias; see Deprecated Fields (optional, see also Format)
architectureFilter the replacement browse view by model architecture. Accepts 1 (A1), 2 (A2), or custom. Omitting falls back to A1 + Custom (legacy default; excludes A2). Only applied when the user needs to browse for a replacement. (optional)
menubarSet to true to show a navigation bar at the top of the TONE3000 experience with back, forward, refresh, and close buttons. Recommended for in-app browsers and popup windows (optional)
login_hintOptional. Pre-fills the email field if the user must sign in. Same rules as Authentication; malformed or overlong values are ignored.

Step 2: TONE3000 Verifies Access

After sign-in, TONE3000 checks whether the user can access the requested tone: public tones, tones they own, and tones they've favorited all proceed immediately. If the tone is private or deleted, TONE3000 shows a friendly error page with the option to browse the catalog and pick a replacement. Any gears or format filters you passed are applied to that replacement browse view.

When a user selects a replacement tone, the callback is the same as a successful load. The tone_id in the callback will be the newly selected tone, not the one you originally requested.

Step 3: Handle the Callback

TONE3000 redirects to your redirect_uri with a code, state, and the resolved tone_id. Always verify state before proceeding. Note that tone_id may differ from your original request if the user selected a replacement.

If the menubar is enabled and the user clicks the close button, the redirect will include canceled=true instead of a tone_id. If the user had already signed in, a code is still included and can be exchanged for tokens. If the user closed before signing in, no code is present.

const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
const toneId = params.get('tone_id'); // may differ from your original request
const canceled = params.get('canceled') === 'true';
if (state !== sessionStorage.getItem('t3k_state')) {
throw new Error('State mismatch. Possible CSRF attack.');
}
if (canceled) {
// User exited without loading a tone.
// If code is present, you can still exchange it for tokens.
// If code is absent, the user closed before signing in.
return;
}

Step 4: Exchange the Code and Fetch the Tone

Exchange the authorization code for an access token (see Token Exchange), then use the tone_id from the callback to fetch tone metadata and model download URLs.

// Token exchange: see Authentication section
const { access_token } = await exchangeCode(code);
// Fetch tone metadata
const tone = await fetch(`https://www.tone3000.com/api/v1/tones/${toneId}`, {
headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json());
// Fetch models, each has a model_url for downloading
const { data: models } = await fetch(
`https://www.tone3000.com/api/v1/models?tone_id=${toneId}`,
{ headers: { Authorization: `Bearer ${access_token}` } },
).then(r => r.json());

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. Optionally filter by gear type.

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)
    gearGearOptional. Gear type to filter by, e.g. amp-cab. When omitted, returns tones of all gear types. A single value

Favorited Tones

Get a list of tones favorited by the currently authenticated user. Optionally filter by gear type.

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)
    gearGearOptional. Gear type to filter by, e.g. amp-cab. When omitted, returns tones of all gear types. A single value

Downloaded Tones

Get a list of tones downloaded by the currently authenticated user. Duplicate downloads of the same tone are collapsed, so each tone appears once. Optionally filter by gear type.

const response = await fetch(`https://www.tone3000.com/api/v1/tones/downloaded?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)
    gearGearOptional. Gear type to filter by, e.g. amp-cab. When omitted, returns tones of all gear types. A single value

Get Tone

Get a single tone by ID. Public tones are accessible to any authenticated user. Private tones are only accessible to the owner or users who have favorited the tone.

// architecture is optional: 1 | 2 | 'custom'. Omit for legacy A1 + Custom.
const response = await fetch(`https://www.tone3000.com/api/v1/tones/${toneId}?architecture=${architecture}`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
const tone = await response.json();
  • Response type: Tone
  • To get download URLs for a tone's models, use the List Models endpoint.
  • Path parameters:
    NameTypeDescription
    idnumberID of the tone to retrieve
  • Query parameters:
    NameTypeDescription
    architectureArchitectureFilter the tone's models_count by architecture. Accepts 1 (A1), 2 (A2), or custom. Omitting returns A1 + Custom (legacy default; excludes A2). Per-architecture breakdown columns (a1_models_count, a2_models_count,custom_models_count) are always returned so you can compute your own visible count. Applies to NAM tones only; ignored for other formats. Use the List Models endpoint with the same parameter to fetch the matching model objects. (optional)

Favorite Tone

Favorite a tone on behalf of the authenticated user. Idempotent: returns 200 with the favorite whether it was just created or already existed. Only public tones or tones you own can be favorited.

const response = await fetch(`https://www.tone3000.com/api/v1/tones/${toneId}/favorite`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
const favorite = await response.json();
  • Response type: Favorite (status 200)
  • Path parameters:
    NameTypeDescription
    idnumberID of the tone to favorite

Unfavorite Tone

Remove a tone from the authenticated user's favorites. Idempotent: returns 204 No Content whether or not the tone was favorited.

await fetch(`https://www.tone3000.com/api/v1/tones/${toneId}/favorite`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
  • Returns 204 No Content with no response body.
  • Path parameters:
    NameTypeDescription
    idnumberID of the tone to unfavorite

Search Tones

Search and filter tones with various filters and sorting options.

This endpoint is heavily rate-limited by default. If you plan to use it in production, please contact support@tone3000.com. We highly recommend using the Select OAuth flow for tone browsing and search rather than this endpoint.
const response = await fetch(`https://www.tone3000.com/api/v1/tones/search?query=${query}&page=${page}&page_size=${pageSize}&sort=${sort}&gears=${gears}&sizes=${sizes}&tags=${tags}&makes=${makes}&creators=${creators}&format=${format}&architecture=${architecture}`, {
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)
    gearsGear[]Filter by gear type. Underscore-separated for multiple values (e.g. amp_amp-cab_pedal). full-rig and ir are deprecated alias values; see Deprecated Fields (optional)
    sizesSize[]Filter by model sizes. Underscore-separated for multiple values (e.g. standard_lite_feather) (optional)
    tagsstring[]Filter by tag name, matched exactly against the values in a tone's tags array. Underscore-separated for multiple values (e.g. clean_high-gain). Multiple values are OR'd: a tone matches if it carries any of them (optional)
    makesstring[]Filter by make and model name, matched exactly against the values in a tone's makes array. Underscore-separated for multiple values (e.g. Fender Twin Reverb_1965 Vox AC30). Multiple values are OR'd (optional)
    creatorsstring[]Filter by creator, matched exactly against a tone's user.username. Comma-separated for multiple values (e.g. tone3000,amalgamaudio), not underscore-separated, because usernames may themselves contain _ and -. Multiple values are OR'd (optional)
    formatFormatRestrict results to a single tone format (e.g. nam, ir). platform is a deprecated alias; see Deprecated Fields (optional; omit for all formats)
    architectureArchitectureFilter results by model architecture. Accepts 1 (A1), 2 (A2), or custom. Omitting returns tones with A1 or Custom models (legacy default; excludes A2-only tones). (optional)
    calibratedbooleanRestrict results to tones that have at least one calibrated model. Pass true to enable (optional; default returns all)

Get the top 10 trending tones, the same feed as the trending lanes on the TONE3000 homepage. Optionally filter by gear type. Not paginated; always returns at most 10 tones, sorted by trending score.

// Top trending across all gear types
const response = await fetch('https://www.tone3000.com/api/v1/tones/trending', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
// Or filter by gear: /api/v1/tones/trending?gear=${gear}
const { data: tones } = await response.json();
  • Response type: { data: Tones[] }
  • Only public tones with at least one downloadable model, an image, and a description are included.
  • Query parameters:
    NameTypeDescription
    gearGearOptional. Gear type to filter the trending feed, e.g. amp-cab. When omitted, returns the top 10 trending tones across all gear types. A single value

Latest Tones

Get the 10 most recently published tones, the same feed as the latest section on the TONE3000 homepage. Not paginated; always returns at most 10 tones, sorted by publish date descending.

const response = await fetch('https://www.tone3000.com/api/v1/tones/latest', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
const { data: tones } = await response.json();
  • Response type: { data: Tones[] }
  • Covers nam and ir tones. Only public tones with at least one downloadable model, an image, and a description are included.
  • No query parameters.

Download Tone

This endpoint is available to approved partners only. Requests from other API clients return 403. For nearly all integrations, download individual models via the model_url field from List Models instead: it gives you per-model control and works for every API client. If your use case genuinely requires whole-tone zip archives, contact support@tone3000.com.

Get a download URL for a zip archive containing all of a tone's models. Public tones are accessible to any authenticated user. Private tones are only accessible to the owner or users who have favorited the tone.

const response = await fetch(`https://www.tone3000.com/api/v1/tones/${toneId}/download`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
const { url, expires_at, filename } = await response.json();
// url is a temporary link to the zip archive, no auth header needed
const zip = await fetch(url);
  • Response fields:
    NameTypeDescription
    urlstringTemporary download URL for the zip archive. Requires no authentication and expires one hour after being issued. Request it when the user initiates a download rather than storing it.
    expires_atstringISO 8601 timestamp at which url stops working
    filenamestringSuggested filename for the archive, derived from the tone title (e.g. My Tone.zip)
  • The archive contains every model in the tone, named after the model (deduplicated with a numeric suffix where needed). These are the same files and names as downloading the tone on tone3000.com.
  • For tones with format nam, the archive contains A2 files only. Other formats include all of the tone's models.
  • Archives are cached server-side and rebuilt automatically when the tone's models change, so the first request after a change may take a few seconds while subsequent requests are fast.
  • Returns 400 if the tone has no downloadable models.
  • Path parameters:
    NameTypeDescription
    idnumberID of the tone to download

Models

Get Model

Get a single model by ID. Accessible if the parent tone is public, owned by the authenticated user, or favorited by the authenticated user.

const response = await fetch(`https://www.tone3000.com/api/v1/models/${modelId}`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
const model = await response.json();
  • Response type: Model
  • The model_url field is a pre-built download URL. Pass your access token as a Bearer token when fetching it.
  • Path parameters:
    NameTypeDescription
    idnumberID of the model to retrieve

List Models

Get a list of models for a specific tone accessible by the current authenticated user.

// architecture is optional: 1 | 2 | 'custom'. Omit for legacy A1 + Custom.
const response = await fetch(`https://www.tone3000.com/api/v1/models?tone_id=${toneId}&page=${page}&page_size=${pageSize}&architecture=${architecture}`, {
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: 300, optional)
    architectureArchitectureFilter by model architecture. Accepts 1 (A1), 2 (A2), or custom. Omitting the parameter returns A1 + Custom (legacy default; excludes A2).

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

In the search API, use underscore-separated values for multiple gear types (e.g. amp_amp-cab_pedal). Comma-separated is deprecated. Two value-level deprecations to be aware of (see Deprecated Fields): full-rig is accepted as an alias for amp-cab and is normalized on persist; ir as a Gear is stripped from gears on persist andformat=ir is inferred when no format was supplied (explicit format wins).

enum Gear {
Amp = 'amp',
AmpCab = 'amp-cab',
Pedal = 'pedal',
Outboard = 'outboard',
Cab = 'cab',
Space = 'space',
Experimental = 'experimental',
// Deprecated: accepted on input, normalized on persist.
FullRig = 'full-rig', // alias for AmpCab
Ir = 'ir', // stripped from gears; use Format=ir
}

Architecture

Model architecture version. Values are the strings '1' (A1), '2' (A2), or 'custom'. Omitting the query parameter falls back to A1 + Custom (the legacy default before A2 was introduced).

enum Architecture {
A1 = '1',
A2 = '2',
Custom = 'custom'
}

Format

enum Format {
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

In the search API, use hyphen-separated values for multiple sizes (e.g. standard-lite-feather). Comma-separated is deprecated.

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'
}

Deprecated Fields

These query parameters and field values are accepted permanently for backwards compatibility but are normalized on persist. Responses to deprecated requests carry the following headers so SDKs can flag them without inspecting bodies:

  • Deprecation: true (per RFC 8594)
  • Link: <…>; rel="deprecation" pointing to this section
  • X-Tone3000-Deprecations: comma-separated list of the specific kinds that fired (e.g. legacy_platform_key,legacy_ir_gear_value)
FieldReplacementEndpointsNotes
?platform=…?format=…All endpoints that accept FormatAccepted permanently as a deprecated alias. The response always emits format; the platform field is no longer included in response bodies.
?gear=…?gears=…Search, Select, Load ToneSingular ?gear= is accepted but rewritten to ?gears=. On the /search page, a 308 redirect rewrites the URL to canonical form.
gears=irformat=irSearch, Select, Load ToneThe ir Gear value is deprecated. Any ir token is removed from the gears filter before the query runs (or on persist, for the OAuth Select / Load Tone flows). When no format is supplied, format=ir is inferred as the intent. An explicit format always wins.
gears=full-riggears=amp-cabSearch, Select, Load ToneAccepted as a permanent deprecated alias. The runtime treats full-rig and amp-cab as the same logical Gear: responses always emit amp-cab, and the search filter is symmetric (querying either returns rows of both during the transition window).

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;
}

Favorite

interface Favorite {
id: number;
tone_id: number;
user_id: string;
created_at: string;
}

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;
format: Format;
license: License;
sizes: Size[];
makes: Make[];
tags: Tag[];
models_count: number;
a1_models_count: number;
a2_models_count: number;
irs_count: number;
custom_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;
// Architecture for NAM models; null for non-NAM (e.g. IR).
architecture_version: Architecture | null;
}

Example App

The example repository contains self-contained demo apps, one for each integration flow, along with tone3000-client.ts, a zero-dependency helper that covers all OAuth flows and API endpoints.

Acme Inc: Select Flow

User browses the TONE3000 catalog and picks a tone. The app receives the selected tone_id and fetches tone metadata and model download URLs.

Beacon Inc: Load Tone Flow

App stores TONE3000 tone IDs in presets and loads them on demand. TONE3000 handles auth and access; if a tone is unavailable, the user can pick a replacement.

Chord Inc: Full API Integration

Reference implementation covering every documented endpoint: search, tone detail, user profile, favorites, model listings, and file downloads.

tone3000-client.ts

src/tone3000-client.ts is a zero-dependency integration helper included as inspiration for your own integration. It covers PKCE generation, OAuth flows, automatic token refresh, and authenticated requests via T3KClient.

import { startSelectFlow, handleOAuthCallback, T3KClient } from './tone3000-client';
// Start a flow
await startSelectFlow(PUBLISHABLE_KEY, REDIRECT_URI);
// Handle the callback
const result = await handleOAuthCallback(PUBLISHABLE_KEY, REDIRECT_URI);
if (result.ok) {
client.setTokens(result.tokens);
const { toneId } = result;
}
// Make authenticated API requests
const client = new T3KClient(PUBLISHABLE_KEY, () => {
startSelectFlow(PUBLISHABLE_KEY, REDIRECT_URI); // called when re-auth is needed
});
const tone = await client.getTone(toneId);
const { data: models } = await client.listModels(toneId);
await client.downloadModel(models[0].model_url, models[0].name);

Design Requirements

Every TONE3000 integration follows these core design requirements. They keep the experience consistent for users, protect creator attribution, and make integration sign-off fast. The wireframes below show reference layouts for desktop and mobile. They are illustrative, so match them to your product's own design language and replace the placeholder content (Your Logo, Gear title, @username) with real integration content.

1Entry point

The TONE3000 entry point is associated with adding a tone to a signal block. Users reach TONE3000 from the place where they add or change a tone in their signal chain.

Desktop
Signal Chain
+
Block
+
Block (selected)
HOST TONES
Preloaded tone 1
Preloaded tone 2
Preloaded tone 3
Tone3000 - NAM Captures and IRsBrowse tones
+
Block
+
Block
Mobile
Signal Chain
+Block
+Block (selected)
+Block
HOST TONES
Preloaded tone 1
Preloaded tone 2
Preloaded tone 3
Tone3000 - NAM Captures and IRsBrowse tones

2Partnership splash

If the user has not authenticated, show a splash screen introducing the partnership and its value before starting the sign-in flow. Recommended copy:

“[Your brand] has partnered with TONE3000 to give you access to a massive library of Neural Amp Modeler (NAM) captures and IRs of real analog gear, created by a global community of musicians.”

Desktop
Your Logo
×Tone3000 - NAM Captures and IRs

[Your brand] has partnered with TONE3000 to give you access to a massive library of Neural Amp Modeler (NAM) captures and IRs of real analog gear, created by a global community of musicians.

Continue
Mobile
Your Logo
×Tone3000 - NAM Captures and IRs

[Your brand] has partnered with TONE3000 to give you access to a massive library of NAM captures and IRs of real analog gear, created by a global community of musicians.

Continue

3Authentication

After the user hits Continue on the partnership splash, they must authenticate via the TONE3000 Select flow: they enter their email and receive a magic link to sign in.

Desktop
Tone3000 - NAM Captures and IRs
Email address
Send Magic Link
Mobile
Tone3000 - NAM Captures and IRs
Email address
Send Magic Link

4Tone list views

After the user authenticates, a tone list view is the first view they should see. Any list or grid of TONE3000 tones includes, for each tone:

  • Tone image
  • Gear title
  • Gear type (e.g. Amp, Amp + Cab)
  • Format (NAM or IR)
  • Creator (username and avatar)
  • TONE3000 branding on the view

The view also shows the signed-in user's avatar and username, so it's clear they are logged in. Tabs for Favorites, Created, and Downloaded, all available via the CRUD API, let users quickly reach their favorite and own tones. You can also query and render trending and latest tones from the API to help users discover new tones.

  • Where you show a partial list (e.g. suggested or recent tones), include a card or link inviting users to discover more on TONE3000.
  • Always provide a clear, persistent path to browse the full TONE3000 catalog via the Select flow. Suggested and recent tones are a starting point, not a substitute: from anywhere a user sees TONE3000 tones, they can reach the complete library, e.g. the persistent “Browse TONE3000” button shown in the wireframe below.
Desktop
Your Tones@usernameTone3000 - NAM Captures and IRsBrowse TONE3000
FavoritesCreatedDownloaded
tone image
Gear titleAmp + CabNAM@username
tone image
Gear titleAmpNAM@username
tone image
Gear titlePedalNAM@username
tone image
Gear titleCabinetIR@username
Mobile
Your Tones@username
FavoritesCreatedDownloaded
img
Gear titleAmp + CabNAM@username
img
Gear titleAmpNAM@username
img
Gear titlePedalNAM@username
img
Gear titleCabinetIR@username
Tone3000 - NAM Captures and IRsBrowse TONE3000

5Loaded tones in the signal chain

A tone loaded from TONE3000 displays the tone image and the TONE3000 mark within its signal block. Where space allows, also display the tone pack title, gear type, format, and creator.

Desktop
Signal Chain
+
Block
img
Tone3000 - NAM Captures and IRsTone pack titleAmp + CabNAM
Loaded tone
+
Block
+
Block
Mobile
Signal Chain
+Block
img
Tone3000 - NAM Captures and IRsTone pack titleAmp + CabNAM
+Block
+Block

6Tone details

Tapping a tone from a list view or a signal block opens a tone details view. This view includes:

  • Tone pack image
  • Tone pack title
  • Gear type (e.g. Amp, Amp + Cab)
  • Format (NAM or IR)
  • Creator (username and avatar)
  • Model selector

Users should be able to easily switch between models within a tone pack from this view. Where space allows, also display the creator's description.

Desktop
tone pack image
Tone pack title
Amp + CabNAM
@username
DESCRIPTION

Creator's own description of the capture rig, character, and suggested use, shown where space allows… Read more

MODEL
Model name (variant 1)
Model name (variant 1)
Model name (variant 2)
Model name (variant 3)
Mobile
tone pack image
Tone pack title
Amp + CabNAM
@username

Creator's description of the capture rig, character, and suggested use, a few lines truncated where space is tight… Read more

MODEL
Model name (variant 1)

7Logo usage

  • Users must see the full TONE3000 logo before they see the shorter T3K mark, so they have context for what T3K means.
  • Use the TONE3000 logo at entry points, anywhere users might access TONE3000, such as the partnership splash and tone list views.
  • Use the T3K mark in compact placements: in buttons that open the TONE3000 library via the Select flow from a list view, or as a marker on a signal-chain block so users understand the origin of the tone in that block.
Primary logo: show first
Tone3000 - NAM Captures and IRs

Entry points and first exposure:
partnership splash, list view headers

Compact mark: after context
Tone3000 - NAM Captures and IRs

Compact placements: buttons,
signal-block markers

Examples
Button: opens the Select flowTone3000 - NAM Captures and IRsBrowse TONE3000
Signal block: tone origin marker
img
Tone3000 - NAM Captures and IRsTone pack title

Download TONE3000 Logos for the official logo and mark assets to use in your integration.

Commercial Terms

Evaluating and developing against the API is free for everyone. Shipping a product falls into one of two tiers:

Free

You may build and publish TONE3000-enabled products for free if your product is non-commercial. This means free and open software or hardware with no paid product, or upsell. Examples include open source projects, DIY guitar pedals, research projects, and community tools. Free tier integrations may only use the OAuth prompt flows (select_tone and get_tone) and bounded list endpoints (favorited, downloaded, created, trending and latest). TONE3000 may, at its discretion, provide design, engineering, or promotional support for outstanding free integrations.

Commercial

If you charge for your product, or your product promotes or accompanies a paid product, a commercial agreement is required. Commercial products may use the full API, including all CRUD endpoints. Contact us at support@tone3000.com to learn more.

Commercial integrations must be reviewed and signed off by TONE3000 before they are publicly published or announced. Free tier integrations do not require sign-off, but access may be revoked if the integration does not follow guidelines.