All requests must be made over HTTPS and will be authenticated using an API key. To create your first API Key, please use the AudioStack Platform.
Keep your API keys secret!They should not be added to the client-side code or checked into your application's code, including through repositories such as Github.
import audiostack
import os
audiostack.api_key = os.environ["AUDIO_STACK_DEV_KEY"]Code Examples
Get your first audio project started with the below example. This is also useful for debugging if your API key is working.
Create a script
This creates a project called myFirstAudioProject with one module admodule under which you'll find your first script.
curl -X 'POST' \
'https://v2.api.audio/content/script' \
-H 'accept: application/json' \
-H 'x-api-key: APIKEY' \
-H 'Content-Type: application/json' \
-d '{
"projectName": "myFirstAudioProject",
"moduleName": "admodule",
"scriptName": "script1",
"scriptText": "Got my first script created!"
}'Retrieve uploaded custom audio files
curl --request GET \
--url 'https://v2.api.audio/content/media' \
--header 'Accept: application/json' \
--header 'x-api-key: APIKEY'Multi-Organization API Access Guide
Overview
This guide explains how to work with the AudioStack API when your account belongs to more than one organization. It covers the x-assume-org header, common error patterns, and best practices for avoiding permissions issues.
1. How Organizations Work
Every AudioStack user belongs to at least one organization. An organization is typically your company or team. Resources you create — audioforms, stories, sound templates, etc. — are owned by the organization that was active when you created them.
When your account is associated with a single organization, the API automatically scopes all requests to that org. No extra configuration is needed.
When your account is associated with multiple organizations, the API must know which org context to use for each request. Without explicit instruction, it falls back to your default organization. If the resource or endpoint you are targeting belongs to a different org, the API denies the request.
Key concepts
| Concept | Description |
|---|---|
| Organization ID | A unique identifier for each org (e.g., my-team-a1b2-c3d4). |
| Default organization | The org the API uses when no x-assume-org header is provided. This is set at the API key or account level. |
x-assume-org header | A request header that overrides the default org for that specific API call. |
| API key | Authenticates your identity. A single key can have access to multiple orgs, but it always has one default. |
2. The Problem: 403 "Access denied. Invalid org permissions."
When making API requests, you may receive a 403 error:
{
"message": "Access denied. Invalid org permissions."
}Or, wrapped by a client-side handler:
{
"message": "Story API request failed with status 403",
"error": "{ \"message\": \"Access denied. Invalid org permissions.\" }"
}Why this happens
The API validated your API key successfully (otherwise you would get a 401 Unauthorized), but the organization context of the request does not match the organization that owns the resource or endpoint. This means:
- Your API key is valid.
- The org the API is using for the request does not have permission to access what you asked for.
Common scenarios that trigger this error
| Scenario | What happened |
|---|---|
| Resource created under a different org | You created an audioform or story under Org A, then tried to retrieve it while your API key defaults to Org B. |
| Old API key, new org | Your original API key defaults to an older org. New endpoints or resources were provisioned under a newer org. The key is valid, but the default org is wrong. |
| Staging vs. production | Your staging and production environments are tied to different organizations. A request intended for one environment is accidentally scoped to the other. |
| Agency or multi-tenant setup | You manage multiple client organizations. Without specifying which org to assume, the API defaults to your own org rather than the client's. |
3. The Fix: Use the x-assume-org Header
x-assume-org HeaderInclude the x-assume-org header in your API requests, set to the Organization ID that owns the resource or endpoint you are calling.
Example: cURL
curl -X GET "https://v2.api.audio/audioforms/{audioformId}" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-assume-org: my-team-a1b2-c3d4"Example: POST request (creating a story)
curl -X POST "https://v2.api.audio/creator/story" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-assume-org: my-team-a1b2-c3d4" \
-H "Content-Type: application/json" \
-d '{
"scriptText": "Your story content here..."
}'Example: Python (requests)
import requests
headers = {
"x-api-key": "YOUR_API_KEY",
"x-assume-org": "my-team-a1b2-c3d4",
"Content-Type": "application/json",
}
response = requests.get(
"https://v2.api.audio/audioforms/{audioformId}",
headers=headers,
)Example: JavaScript (fetch)
const response = await fetch(
"https://v2.api.audio/audioforms/{audioformId}",
{
method: "GET",
headers: {
"x-api-key": "YOUR_API_KEY",
"x-assume-org": "my-team-a1b2-c3d4",
"Content-Type": "application/json",
},
}
);4. Finding Your Organization ID
- Log in to the AudioStack dashboard.
- Navigate to Settings > Organization (or the equivalent section).
- Copy the Organization ID — it will look something like
my-team-a1b2-c3d4.
If you belong to multiple orgs, you will see each one listed. Make sure you select the org that owns the resource you are trying to access.
Tip: If you are unsure which org a resource belongs to, check where (and by whom) it was originally created. Resources are always tied to the org that was active at the time of creation.
5. Best Practices
Always send x-assume-org explicitly
x-assume-org explicitlyEven if your API key currently defaults to the correct org, explicitly setting the header protects you against:
- Future changes to your default org.
- Key rotation where the new key has a different default.
- Teammates using different keys with different defaults.
Set it once in your HTTP client configuration
Rather than adding the header to every individual request, configure it at the client or session level:
import requests
session = requests.Session()
session.headers.update({
"x-api-key": "YOUR_API_KEY",
"x-assume-org": "my-team-a1b2-c3d4",
})
# All requests through this session will include the header
response = session.get("https://v2.api.audio/audioforms/{audioformId}")Use environment variables for org IDs
Avoid hardcoding Organization IDs. Store them alongside your API key:
export AUDIOSTACK_API_KEY="your-api-key"
export AUDIOSTACK_ORG_ID="my-team-a1b2-c3d4"import os
headers = {
"x-api-key": os.environ["AUDIOSTACK_API_KEY"],
"x-assume-org": os.environ["AUDIOSTACK_ORG_ID"],
}Keep org IDs consistent across environments
If you use separate orgs for staging and production, make the org ID part of your environment configuration so it cannot be accidentally swapped:
| Environment | API Key Variable | Org ID Variable |
|---|---|---|
| Staging | AUDIOSTACK_API_KEY_STAGING | AUDIOSTACK_ORG_ID_STAGING |
| Production | AUDIOSTACK_API_KEY_PROD | AUDIOSTACK_ORG_ID_PROD |
6. Troubleshooting Checklist
Use this step-by-step checklist when you encounter a 403 org permissions error:
| # | Step | What to check |
|---|---|---|
| 1 | Confirm the error | Is the message specifically "Access denied. Invalid org permissions."? A generic 403 may have a different cause. |
| 2 | Check for x-assume-org | Is the header present in your request? Log or inspect your outgoing headers to verify. |
| 3 | Verify the Org ID | Is the Organization ID spelled correctly? Org IDs are case-sensitive. |
| 4 | Confirm key-to-org access | Does your API key have access to the organization you are specifying? A valid key paired with an org it cannot access will still return a 403. |
| 5 | Confirm resource ownership | Was the resource (audioform, story, etc.) created under the org you are specifying? Resources do not transfer between orgs automatically. |
| 6 | Check for multiple keys | Are you accidentally using a different API key than intended? Different keys may have different default orgs and different org access. |
| 7 | Test with a minimal request | Strip your request down to the simplest possible call (e.g., a GET on a known resource) with the x-assume-org header to isolate the issue. |
7. Summary of Required Headers
For multi-org API access, every request should include:
x-api-key: YOUR_API_KEY
x-assume-org: YOUR_ORGANIZATION_ID
Content-Type: application/json (for POST/PUT/PATCH requests)
8. FAQ
Q: Do I need this header if I only belong to one organization?
A: No. The header is only required when your API key is associated with multiple organizations. If you belong to a single org, requests are automatically scoped to it. However, adding it anyway does no harm and future-proofs your integration.
Q: I'm sending the header but still getting a 403.
A: Double-check:
- The Organization ID is spelled correctly (case-sensitive).
- Your API key actually has access to that organization.
- The resource you are accessing was created under the org you are specifying.
- You are not accidentally overriding the header elsewhere in your code (e.g., a middleware or interceptor).
Q: I switched API keys and the error went away. Why?
A: Different API keys can have different default organizations. The new key likely defaults to the correct org, making the x-assume-org header unnecessary. However, relying on key defaults is fragile — always set the header explicitly.
Q: Does this apply to all API versions and endpoints?
A: Yes. The x-assume-org header is supported across API versions (v1 and v2) and all endpoints, including /audioforms, /creator/story, and others.
Q: Can I switch orgs mid-session or mid-workflow?
A: Yes. The x-assume-org header is evaluated per-request. You can target different orgs in consecutive requests by changing the header value. There is no persistent session state to reset.
Q: What is the difference between a 401 and a 403 in this context?
A: A 401 Unauthorized means your API key is invalid or missing. A 403 Forbidden with the "Invalid org permissions" message means your key is valid but the org context is wrong. If you see a 401, fix your API key first before troubleshooting org headers.

