Mobile Integration
Use ADN's API with Client-Side API Keys from your iOS, Android, or Flutter app. Play and upload with native media elements.
Best For
Playing Tracks
Create play sessions and stream audio with native media players.
Call ADN's playback API directly from your mobile app using a Client-Side API Key. Create play sessions, fetch signed variant URLs, and stream audio with native media players.
1. Create a Client-Side Player API Key
Under Settings → API Keys, create a Client-Side Player key. Scope it to the variants and content your mobile app should access.
2. Create a Play Session
import Foundation
struct PlaySessionRequest: Encodable {
let collectionId: String
let variants: [String]
let isDownloadable: Bool
let expiresIn: Int
enum CodingKeys: String, CodingKey {
case collectionId = "collection_id"; case variants
case isDownloadable = "is_downloadable"; case expiresIn = "expires_in"
}
}
struct PlaySessionResponse: Decodable {
let playSessionId: String
let tracks: [Track]
enum CodingKeys: String, CodingKey { case playSessionId = "play_session_id"; case tracks }
struct Track: Decodable { let id: String }
}
func createPlaySession() async throws -> (playSessionId: String, tracks: [PlaySessionResponse.Track]) {
var request = URLRequest(url: URL(string: "https://api.audiodelivery.net/v1/play_session/collection")!)
request.httpMethod = "POST"
request.setValue("Bearer CLIENT-SIDE-PLAYER-API-KEY", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
request.httpBody = try encoder.encode(PlaySessionRequest(
collectionId: "COLLECTION-ID", variants: ["hq", "lq"], isDownloadable: false, expiresIn: 3600))
let (data, _) = try await URLSession.shared.data(for: request)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let session = try decoder.decode(PlaySessionResponse.self, from: data)
return (session.playSessionId, session.tracks)
} 3. Fetch Track Variant URLs
struct TrackVariantsResponse: Decodable {
let variants: [Variant]
struct Variant: Decodable {
let url: String
let variant: VariantIndex
struct VariantIndex: Decodable { let id: String }
}
}
// No auth needed — the session ID acts as a bearer token
func getAudioUrl(playSessionId: String, trackId: String) async throws -> String {
let url = URL(string: "https://api.audiodelivery.net/v1/play/\(playSessionId)/\(trackId)")!
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode(TrackVariantsResponse.self, from: data)
let hq = res.variants.first { $0.variant.id == "hq" }!
return hq.url
} 4. Play with Native Player
import AVFoundation
let player = AVPlayer(url: URL(string: audioUrl)!)
player.play() The signed URL works with any native media player. ADN handles CDN delivery and URL verification.
Waveform Data
levels field in the track response contains the data — use it to render waveforms
in your own player, or generate custom waveforms via the Audio Analysis variant type.
Cover Images & Theme Colors
cover_image
in the track response (in icon/small/regular/large sizes). You can also upload a cover image
separately via the API. When a cover image is present, ADN extracts a palette of colors
and selects a primary player_color — use these to style your player UI. Alongside it,
responses include player_color_light and player_color_dark, contrast-adjusted
variants for drawing the color on light and dark backgrounds respectively. The full
theme array contains all extracted colors with hex values, area, lightness, and saturation data.
Uploading Tracks
Upload audio files from native apps using ADN's upload API.
Call ADN's upload API directly from your mobile app using a Client-Side API Key. Create upload sessions, get presigned URLs, and upload audio files — all from native code.
1. Create a Client-Side Upload API Key
Login to ADN, go to Settings → API Keys, and create a Client-Side Upload key. Scope it to the collection your app should upload to.
2. Create an Upload Session
import Foundation
struct UploadSessionRequest: Encodable {
let collectionId: String
let expiresIn: Int
enum CodingKeys: String, CodingKey { case collectionId = "collection_id"; case expiresIn = "expires_in" }
}
struct UploadSessionResponse: Decodable {
let uploadSession: UploadSession
enum CodingKeys: String, CodingKey { case uploadSession = "upload_session" }
struct UploadSession: Decodable { let id: String }
}
func createUploadSession() async throws -> String {
var request = URLRequest(url: URL(string: "https://api.audiodelivery.net/v1/upload_session")!)
request.httpMethod = "POST"
request.setValue("Bearer CLIENT-SIDE-UPLOAD-API-KEY", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
request.httpBody = try encoder.encode(UploadSessionRequest(collectionId: "COLLECTION-ID", expiresIn: 3600))
let (data, _) = try await URLSession.shared.data(for: request)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let session = try decoder.decode(UploadSessionResponse.self, from: data)
return session.uploadSession.id
} 3. Get a Track Upload URL
struct TrackUploadRequest: Encodable {
let fileName: String
enum CodingKeys: String, CodingKey { case fileName = "file_name" }
}
struct TrackUploadResponse: Decodable {
let trackUpload: TrackUpload
enum CodingKeys: String, CodingKey { case trackUpload = "track_upload" }
struct TrackUpload: Decodable { let uploadUrl: String; let method: String
enum CodingKeys: String, CodingKey { case uploadUrl = "upload_url"; case method }
}
}
func getTrackUploadUrl(sessionId: String) async throws -> (url: String, method: String) {
var request = URLRequest(url: URL(string: "https://api.audiodelivery.net/v1/upload/\(sessionId)/track")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
request.httpBody = try encoder.encode(TrackUploadRequest(fileName: "recording.m4a"))
let (data, _) = try await URLSession.shared.data(for: request)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode(TrackUploadResponse.self, from: data)
return (res.trackUpload.uploadUrl, res.trackUpload.method)
} 4. Upload the File
// Prefer streaming for large files instead of loading into memory
func uploadAudio(fileURL: URL, uploadURL: URL, method: String) async throws {
var request = URLRequest(url: uploadURL)
request.httpMethod = method
request.setValue("audio/mp4", forHTTPHeaderField: "Content-Type")
request.httpBodyStream = InputStream(url: fileURL)
let (_, uploadResponse) = try await URLSession.shared.data(for: request)
let httpResponse = uploadResponse as! HTTPURLResponse
print("Upload status: \(httpResponse.statusCode)") // 200 on success
} Repeat steps 3 and 4 for each file — every track needs its own upload request and signed URL.
5. Wait Until the Track Is Ready
Processing begins automatically after the upload. Before you play the track, wait until it is ready — poll GET /v1/track/{track_id} until track.track_status_id is ready, or configure a webhook (next step) to be notified instead of polling.
6. Optional: Webhooks
Add a webhook under Settings → Webhook to be notified when a track reaches a terminal outcome (ready, incomplete, error, or init_error) and when its full file set is complete. See the Track Processing webhook docs.
Cover Images
theme)
that can be used to style your player UI.