Introducing the Aden SDK: Type-Safe Access to Your Music Workflow
Meet @adenspace/sdk: a type-safe TypeScript client for the Aden API. Learn how teams can integrate their music catalog into custom apps, and how we use it to power our own mobile and desktop clients.

Aden is a collaborative workspace for artists and music teams. We handle tracks, versions, albums, collaborators, splits, feedback, releases. Everything that used to live across Drive folders, WhatsApp threads, and spreadsheets. Today, we're opening that same data layer to developers with @adenspace/sdk.
Why an SDK?
As Aden grew, we kept hitting the same problem internally: our web app, mobile app, and desktop client all needed to talk to the same API, and each one was writing its own fetch wrappers, auth logic, and type definitions. Every time we added an endpoint, we had to update three codebases.
We also started hearing from teams who wanted to build on top of Aden. Custom dashboards for managers, automated release pipelines, integrations with DAWs, bots that notify collaborators. They needed a clean, documented API. We needed one too.
So we built @adenspace/sdk: and we use it ourselves.
What Is It?
The SDK is a single TypeScript package that gives you type-safe access to the entire Aden API. No code generation, no build step, no schema syncing. You install it, create a client, and start making calls with full autocomplete.
Under the hood, it uses Elysia Eden Treaty to derive types directly from our API definition. When we add a field to a track response, your editor knows about it the moment you update the package. When we add a query parameter, it shows up in autocomplete. When you pass the wrong type, it fails at compile time, not at runtime.
bun add @adenspace/sdk
# or
npm install @adenspace/sdk
Getting Started
There are two ways to authenticate: API keys for server-to-server integrations, and Supabase tokens for apps where users sign in.
With an API Key
Generate a team API key from your team settings in the Aden dashboard. Keys are prefixed with aden_live_ and scoped to a single team.
import { createAdenClient } from '@adenspace/sdk'
const Aden = createAdenClient({
apiKey: 'aden_live_xxx',
})
// List tracks for your team
const { data } = await aden.api.v1.tracks.get({
query: { teamId: '123' },
})
// Get a specific track with audio URL and cover art
const { data: track } = await aden.api.v1.tracks({ id: 1 }).get()
With Supabase Auth
If you're building an app where users log in with their Aden account, pass a dynamic token provider instead:
import { createClient } from '@supabase/supabase-js'
import { createAdenClient } from '@adenspace/sdk'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
const Aden = createAdenClient({
getToken: async () => {
const { data } = await supabase.auth.getSession()
return data.session?.access_token ?? null
},
})
// Now all calls are authenticated as the signed-in user
const { data: me } = await aden.api.v1.auth.me.get()
const { data: teams } = await aden.api.v1.teams.get()
const { data: tracks } = await aden.api.v1.tracks.get({
query: { teamId: '123', limit: '10' },
})
The getToken function is called on every request, so token refresh is handled automatically by Supabase.
What You Can Do
The API covers the core Aden data model. Here's what's available today:
Tracks
The central resource. A track goes through statuses: idea → in_progress → ready_to_be_released → released. Each track can have multiple versions, collaborators with split percentages, and cover art.
// List all tracks for a team
const { data } = await aden.api.v1.tracks.get({
query: { teamId: '42', limit: '20', offset: '0' },
})
// Each track includes:
// - audio_url (presigned, ready to stream)
// - cover_url / cover_url_medium (optimized sizes)
// - status, description, metadata
Albums
Group tracks into albums with ordering, cover art, and release metadata like UPC codes.
// List albums
const { data } = await aden.api.v1.albums.get({
query: { teamId: '42' },
})
// Get a single album with its full tracklist and audio URLs
const { data: album } = await aden.api.v1.albums({ id: 5 }).get()
Versions
Every track can have multiple versions. Rough demo, mix V2, final master. The SDK lets you list versions, get waveform data for visualization, and manage which version is the "final" cut.
// List all versions for a track
const { data } = await aden.api.v1.tracks({ id: 1 }).versions.get()
// Each version includes:
// - audio_url (presigned)
// - waveform (normalized float array for visualization)
// - version_number, lyrics, final flag
// Get timestamped comments on a specific version
const { data: comments } = await aden.api.v1
.tracks({ id: 1 })
.versions({ versionId: 7 })
.comments.get()
Shares
Create secure, expiring share links for tracks and albums. Control whether recipients can download files, leave comments, or see full metadata.
// Create a share link for a track
const { data } = await aden.api.v1.shares.tracks({ trackId: 1 }).post({
allowDownloads: true,
allowComments: true,
expire: '2026-03-01',
email: 'label@example.com',
title: 'New single - rough mix',
})
console.log(data.shareUrl)
// → https://aden.space/@yourteam/shares/tracks/1?token=...
How We Use It
This is not an SDK we built for other people and forgot about. It powers our own apps.
Mobile App
Our React Native mobile app imports @adenspace/sdk directly. The entire API layer is 13 lines:
// apps/mobile/src/lib/aden.ts
import { createAdenClient } from '@adenspace/sdk'
import { supabase } from './supabase'
import { API_BASE_URL } from './constants'
export const Aden = createAdenClient({
baseUrl: API_BASE_URL,
getToken: async () => {
const {
data: { session },
} = await supabase.auth.getSession()
return session?.access_token ?? null
},
})
That's it. No custom fetch wrapper, no separate type definitions, no API client to maintain. When we add a new endpoint to the API, the mobile app gets type-safe access to it by updating one package.
SDK Playground
We built an internal playground app to test endpoints interactively. Plug in an API key or Supabase token, click a button, see the response. It's the fastest way to verify an endpoint works before writing any integration code.
Web App
Our Next.js web app calls the same API through server actions and cached queries. The API definition is the single source of truth. The SDK simply exposes it to any JavaScript or TypeScript environment.
Usage Tracking
Every request made with an API key is automatically tracked. We log the method, path, status code, and timestamp to give teams visibility into how their integrations are being used. Requests made with Supabase auth tokens (i.e., your own logged-in users) are not tracked.
This means you can generate API keys for different integrations and see exactly which ones are active, how often they're called, and when they were last used.
The Architecture
For those curious about the internals: the API is built with Elysia, a Bun-native framework. Elysia uses TypeBox schemas for runtime validation, and those same schemas drive the TypeScript types that Eden Treaty extracts.
The flow looks like this:
- We define routes with Elysia, including request/response schemas
- The
Apptype is exported from the API package - The SDK imports that type and passes it to
treaty<App>() - You get a fully typed client. Queries, params, bodies, responses
No codegen. No OpenAPI-to-TypeScript pipeline. Just TypeScript doing what TypeScript does.
We also serve an interactive OpenAPI spec at /api/v1/swagger for teams that prefer REST clients like Postman or Insomnia.
What's Next
This is the first public release of the SDK. We're actively expanding the API surface:
- Write operations via API key: creating tracks, uploading audio, and managing albums through external integrations
- Webhooks: get notified when tracks change status, new versions are uploaded, or albums are released
- Richer metadata: collaborator splits, release links, analytics
- Rate limiting and scoped permissions: granular control over what each API key can access
Get Started
Install the SDK, grab an API key from your team settings, and start building.
bun add @adenspace/sdk
import { createAdenClient } from '@adenspace/sdk'
const Aden = createAdenClient({ apiKey: 'aden_live_xxx' })
const { data } = await aden.api.v1.tracks.get({ query: { teamId: '123' } })
console.log(data)
The full API reference is in the SDK README and the OpenAPI spec is live at aden.space/api/v1/swagger. If you run into anything, open an issue. We're building this in the open.
Less admin. More music.


