# TwoShot Platform API Documentation ## Overview The TwoShot Platform API is a comprehensive REST API for music production, AI-powered audio generation, and sample library management. Built for developers creating music production tools, AI audio applications, and creative platforms. **Base URLs:** - Production: `https://api.twoshot.app` **API Version:** v1 --- ## Use Cases & Integration Scenarios ### 🎵 Music Production Platform Integration Build DAW plugins, web-based music studios, or mobile music creation apps. **Example Flow:** 1. User creates project → `POST /project` 2. Import audio samples → `POST /sample/audio` 3. Build project with tracks/clips → `PUT /project/{id}` 4. Render final audio → `POST /project/{id}/render` ### 🤖 AI Music Generation App Create applications that generate music using AI models. **Example Flow:** 1. Browse available models → `GET /model` 2. Start generation job → `POST /generation` 3. Poll for completion → `GET /generation/{jobId}` 4. Download generated audio → `GET /audio/{audioId}/download` ### 📚 Sample Library Platform Build sample discovery and distribution platforms. **Example Flow:** 1. Browse public samples → `GET /sample` 2. Preview samples → `GET /sample/{sampleId}/preview/{audioId}` 3. Apply licensing → `POST /licence/apply` 4. Download licensed samples → `GET /sample/{sampleId}/download/{audioId}` ### 🎬 Content Creator Tools Integrate music generation into video editing, podcasting, or streaming tools. **Example Flow:** 1. Generate background music → `POST /generation` 2. Create video project → `POST /video` 3. Sync audio with video → `PUT /video/{id}` 4. Export final content → `GET /video/{id}/download` ### 🌐 Social Music Platform Build social platforms for music sharing and collaboration. **Example Flow:** 1. User registration → `POST /auth/register` 2. Share public projects → `PUT /project/{id}` (set public: true) 3. Follow other users → `POST /user/{userId}/follow` 4. Discover trending content → `GET /tag/explore` --- ## Authentication ### Authentication Methods #### 1. Bearer Token (JWT) - Primary Method ```http Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` #### 2. API Key ```http X-API-Key: your-api-key-here ``` --- ## Project Management Create and manage music production projects with full DAW-like functionality. ### Create Project **POST** `/project` Create a new music project with tracks, clips, and audio sources. **Authentication:** Required **Request Body:** ```json { "name": "My New Track", "version": "1.0.0", "tempo": 120.0, "grid": 0.25, "snapToGrid": true, "playHead": { "start": 0.0, "end": 16.0 }, "soloTarget": null, "tracks": [ { "id": "550e8400-e29b-41d4-a716-446655440001", "name": "Drums", "clips": [ { "id": "550e8400-e29b-41d4-a716-446655440002", "name": "Kick Pattern", "color": { "hue": 0.8, "saturation": 0.7, "lightness": 0.5 }, "position": 0.0, "muted": false, "volume": 0.8, "source": { "audioId": "550e8400-e29b-41d4-a716-446655440003", "sampleId": 12345 }, "pitchOffset": 0.0, "reversed": false, "speed": "tempo-sync", "cutStart": 0.0, "cutEnd": 2.5, "pan": 0.0 } ], "pan": 0.0, "loopEvery": "auto", "volume": 0.9, "muted": false } ] } ``` **Response:** ```json { "id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Example Usage:** ```javascript const project = await fetch('/project', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: "My Beat", tempo: 140, tracks: [] }) }); const { id } = await project.json(); console.log('Created project:', id); ``` ### Get Projects **GET** `/project` Retrieve projects with filtering, pagination, and search capabilities. **Authentication:** Optional (returns public projects for anonymous users) **Query Parameters:** - `owner_id` (integer) - Filter by owner ID - `public` (boolean) - Filter by public projects only - `name` (string) - Search by project name - `limit` (integer, max 10, default 10) - Results per page - `offset` (integer, default 0) - Pagination offset - `minTempo` (number) - Minimum BPM filter - `maxTempo` (number) - Maximum BPM filter - `minDuration` (number) - Minimum duration in seconds - `maxDuration` (number) - Maximum duration in seconds **Response:** ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "owner": { "id": 12345, "userName": "musicmaker2024", "verified": true, "featured": false, "displayPicture": { "id": 67890, "url": "https://cdn.twoshot.app/images/profile_67890.jpg" }, "followed": false }, "public": true, "name": "Epic House Track", "version": "2.1.0", "created": "2024-01-15T10:30:00Z", "updated": "2024-01-16T14:45:30Z", "data": { "tempo": 128.0, "grid": 0.25, "tracks": [...] } } ] ``` **Example Usage:** ```javascript // Get public projects with tempo filter const projects = await fetch('/project?public=true&minTempo=120&maxTempo=140&limit=10') .then(r => r.json()); // Get user's own projects const myProjects = await fetch('/project?owner_id=12345', { headers: { 'Authorization': `Bearer ${token}` } }).then(r => r.json()); ``` ### Render Project **POST** `/project/{id}/render` Render project to audio file. **Authentication:** Required **Request Body:** ```json { "format": "wav", "quality": "high", "startTime": 0.0, "endTime": 60.0 } ``` **Response:** ```json { "jobId": "render-job-uuid", "status": "processing", "estimatedCompletion": "2024-01-15T10:35:00Z" } ``` --- ## Audio & Sample Management ### Upload Sample Audio **POST** `/sample/audio` Upload audio file to create a new sample. **Authentication:** Required **Request:** Multipart form data ``` Content-Type: multipart/form-data file: [audio file binary] name: "My Sample" public: true scale: "C Major" ``` **Response:** ```json { "sampleId": 12345, "audioId": "550e8400-e29b-41d4-a716-446655440000", "processing": true } ``` **Example Usage:** ```javascript const formData = new FormData(); formData.append('file', audioFile); formData.append('name', 'My Drum Loop'); formData.append('public', 'true'); const response = await fetch('/sample/audio', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: formData }); ``` ### Get Samples **GET** `/sample` Browse and search sample library. **Authentication:** Optional **Query Parameters:** - `q` (string) - Search query - `owner_id` (integer) - Filter by owner - `public` (boolean) - Public samples only - `scale` (string) - Musical scale filter - `bpm_min` (number) - Minimum BPM - `bpm_max` (number) - Maximum BPM - `duration_min` (number) - Minimum duration - `duration_max` (number) - Maximum duration - `limit` (integer) - Results per page - `offset` (integer) - Pagination offset **Response:** ```json [ { "sample": { "id": 12345, "name": "Deep House Bass", "created": "2024-01-15T10:30:00Z", "audioId": "550e8400-e29b-41d4-a716-446655440000", "ownerId": 67890, "parentFolderId": 555, "public": true, "scale": "A Minor", "imageId": 123 }, "owner": { "id": 67890, "userName": "bassmaster", "verified": true, "displayPicture": { "url": "https://cdn.twoshot.app/images/profile_67890.jpg" } }, "tags": ["house", "bass", "deep", "electronic"], "audioInfo": { "id": "550e8400-e29b-41d4-a716-446655440000", "duration": 8.0, "bpm": 125.0, "free": false, "licenceId": 789 }, "liked": false, "likes": 42 } ] ``` --- ## AI Generation & Models ### Get Available Models **GET** `/model` Retrieve AI models for audio generation. **Authentication:** Optional **Query Parameters:** - `category` (string) - Filter by model category - `sort` (enum) - Sort order: `latest`, `oldest`, `popular`, `random`, `recently_used` - `limit` (integer) - Results per page **Response:** ```json [ { "id": 101, "name": "MusicGen Large", "description": "High-quality music generation model", "category": "music_generation", "tags": ["music", "ai", "generation"], "inputTypes": ["text", "audio"], "outputTypes": ["audio"], "maxDuration": 30.0, "pricing": { "free": false, "credits": 5 }, "parameters": [ { "name": "prompt", "type": "text", "required": true, "description": "Describe the music you want to generate" }, { "name": "duration", "type": "number", "required": false, "default": 10.0, "min": 5.0, "max": 30.0 } ], "liked": false, "likes": 1337 } ] ``` ### Create Generation Job **POST** `/generation` Start AI audio generation job. **Authentication:** Required **Request Body:** ```json { "modelId": 101, "inputs": [ { "type": "text", "value": "Upbeat electronic dance music with heavy bass" }, { "type": "number", "value": 15.0 }, { "type": "audio", "audioId": "550e8400-e29b-41d4-a716-446655440000", "trimStart": 0.0, "trimEnd": 10.0 } ] } ``` **Response:** ```json { "jobId": "550e8400-e29b-41d4-a716-446655440099", "status": "pending", "estimatedCompletion": "2024-01-15T10:35:00Z", "creditsUsed": 5 } ``` **Example Usage:** ```javascript // Generate music from text prompt const generation = await fetch('/generation', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ modelId: 101, inputs: [ { type: "text", value: "Chill lo-fi hip hop beat" }, { type: "number", value: 20.0 } ] }) }); const { jobId } = await generation.json(); // Poll for completion const checkStatus = async () => { const status = await fetch(`/generation/${jobId}`) .then(r => r.json()); if (status.status === 'completed') { console.log('Generated audio:', status.outputs); } else if (status.status === 'failed') { console.error('Generation failed:', status.error); } else { setTimeout(checkStatus, 5000); // Check again in 5 seconds } }; checkStatus(); ``` ### Get Generation Status **GET** `/generation/{jobId}` Check status of generation job. **Authentication:** Optional **Response:** ```json { "id": "550e8400-e29b-41d4-a716-446655440099", "status": "completed", "progress": 100, "created": "2024-01-15T10:30:00Z", "completed": "2024-01-15T10:32:15Z", "model": { "id": 101, "name": "MusicGen Large" }, "inputs": [ { "type": "text", "value": "Upbeat electronic dance music" } ], "outputs": [ { "audioId": "550e8400-e29b-41d4-a716-446655440100", "duration": 15.0, "format": "wav" } ], "error": null, "creditsUsed": 5 } ``` --- ## User Management ### Get Current User **GET** `/user` **Authentication:** Required **Response:** ```json { "id": 12345, "userName": "musicmaker2024", "firstName": "John", "lastName": "Doe", "email": "john@example.com", "created": "2024-01-01T00:00:00Z", "displayPicture": { "id": 67890, "url": "https://cdn.twoshot.app/images/profile_67890.jpg" }, "verified": true, "featured": false, "instagram": "johnmusic", "soundcloudUrl": "https://soundcloud.com/johnmusic" } ``` ### Follow/Unfollow User **POST** `/user/{followedUserId}/follow` **DELETE** `/user/{followedUserId}/follow` **Authentication:** Required **Response:** ```json { "following": true, "followerCount": 1338 } ``` --- ## Licensing & Subscriptions ### Get Available Licenses **GET** `/licence` **Authentication:** Optional **Response:** ```json [ { "id": 1, "name": "Standard License", "description": "Use in commercial projects up to 1M streams", "price": 9.99, "currency": "USD", "terms": { "commercial": true, "streaming_limit": 1000000, "attribution_required": false } } ] ``` ### Apply License **POST** `/licence/apply` Apply license to content for usage rights. **Authentication:** Required **Request Body:** ```json { "licenceId": 1, "contentId": "550e8400-e29b-41d4-a716-446655440000", "contentType": "sample" } ``` **Response:** ```json { "licenseApplicationId": "license-app-uuid", "status": "approved", "validUntil": "2025-01-15T10:30:00Z", "terms": { "commercial": true, "streaming_limit": 1000000 } } ``` --- ## Error Handling ### Error Response Format All errors return JSON with consistent structure: ```json { "error": "Invalid request parameters", "code": "VALIDATION_ERROR", "details": { "field": "tempo", "message": "Tempo must be between 60 and 200 BPM" }, "timestamp": "2024-01-15T10:30:00Z", "path": "/project" } ``` ### HTTP Status Codes - `200` - Success - `201` - Created successfully - `400` - Bad Request (validation errors) - `401` - Unauthorized (invalid/missing token) - `403` - Forbidden (insufficient permissions) - `404` - Not Found - `429` - Rate Limited - `500` - Internal Server Error ### Rate Limiting API requests are rate-limited per user/IP: **Headers:** ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 995 X-RateLimit-Reset: 1642252800 ``` **Rate Limits:** - Anonymous: 100 requests/hour - Authenticated: 1000 requests/hour - Premium: 5000 requests/hour --- ## SDK Examples ### JavaScript/Node.js ```javascript class TwoShotAPI { constructor(token) { this.token = token; this.baseURL = 'https://api.twoshot.app'; } async request(endpoint, options = {}) { const response = await fetch(`${this.baseURL}${endpoint}`, { ...options, headers: { 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/json', ...options.headers } }); if (!response.ok) { throw new Error(`API Error: ${response.status}`); } return response.json(); } // Create project async createProject(projectData) { return this.request('/project', { method: 'POST', body: JSON.stringify(projectData) }); } // Generate audio async generateAudio(modelId, inputs) { return this.request('/generation', { method: 'POST', body: JSON.stringify({ modelId, inputs }) }); } // Upload sample async uploadSample(file, metadata) { const formData = new FormData(); formData.append('file', file); Object.entries(metadata).forEach(([key, value]) => { formData.append(key, value); }); return this.request('/sample/audio', { method: 'POST', body: formData, headers: {} // Let browser set Content-Type for FormData }); } } // Usage const api = new TwoShotAPI('your-token-here'); // Create a project const project = await api.createProject({ name: "My Track", tempo: 128, tracks: [] }); // Generate AI audio const generation = await api.generateAudio(101, [ { type: "text", value: "Upbeat house music" } ]); ``` ### Python ```python import requests import json class TwoShotAPI: def __init__(self, token): self.token = token self.base_url = 'https://api.twoshot.app' self.headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } def request(self, endpoint, method='GET', data=None, files=None): url = f'{self.base_url}{endpoint}' headers = self.headers.copy() if files: headers.pop('Content-Type') # Let requests set it for multipart response = requests.request( method=method, url=url, headers=headers, json=data if not files else None, files=files ) response.raise_for_status() return response.json() def create_project(self, project_data): return self.request('/project', 'POST', project_data) def generate_audio(self, model_id, inputs): return self.request('/generation', 'POST', { 'modelId': model_id, 'inputs': inputs }) def upload_sample(self, file_path, name, public=True): with open(file_path, 'rb') as f: files = {'file': f} data = {'name': name, 'public': str(public).lower()} return self.request('/sample/audio', 'POST', files=files) # Usage api = TwoShotAPI('your-token-here') # Generate audio generation = api.generate_audio(101, [ {"type": "text", "value": "Chill lo-fi beat"} ]) print(f"Generation started: {generation['jobId']}") ``` --- ## Complete Endpoint Reference
Click to expand all 61 public endpoints ### Project Management (8 endpoints) - `POST /project` - Create project - `GET /project` - Get projects with filters - `GET /project/{id}` - Get specific project - `PUT /project/{id}` - Update project - `DELETE /project/{id}` - Delete project - `POST /project/{id}/clone` - Clone project - `POST /project/{id}/render` - Render project - `POST /project/render` - Render project data ### Audio & Sample Management (17 endpoints) - `GET /audio` - Get audio files - `GET /audio/{audioId}` - Get audio metadata - `GET /audio/{audioId}/play` - Get playback URL - `GET /audio/{audioId}/bpm` - Get audio BPM - `GET /audio/{audioId}/download` - Download audio - `GET /audio/{audioId}/stem` - Get stem data - `GET /audio/download` - Bulk download - `GET /sample` - Browse samples - `POST /sample/audio` - Upload sample - `GET /sample/{sampleId}` - Get sample details - `GET /sample/{sampleId}/preview/{audioId}` - Preview sample - `POST/DELETE /sample/{sampleId}/like` - Like/unlike sample - `GET /sample/{sampleId}/download/{audioId}` - Download sample - `PATCH /sample/{sampleId}/visibility` - Update visibility - `PATCH /sample/{sampleId}/rename` - Rename sample - `PATCH /sample/{sampleId}/art` - Update artwork ### AI Generation & Models (9 endpoints) - `GET /model` - Get available models - `GET /model/preset` - Get model presets - `GET /model/tag` - Get model tags - `GET /model/{modelId}` - Get model details - `POST/DELETE /model/{modelId}/like` - Like/unlike model - `POST/DELETE /model/{modelId}/tag/{tag}` - Tag/untag model - `POST /generation` - Create generation job - `GET /generation` - Get user generations - `GET /generation/{jobId}` - Get generation status ### User Management (7 endpoints) - `GET /user` - Get current user - `GET /user/info` - Get user info - `GET /user/referred` - Get referred users - `PATCH /user/picture` - Update profile picture - `PATCH /user/instagram` - Update Instagram - `GET /user/{profileId}` - Get user profile - `POST/DELETE /user/{userId}/follow` - Follow/unfollow ### And 20 more endpoint categories...
--- ## Support & Resources - **API Status:** [status.twoshot.app](https://status.twoshot.app) - **Community Discord:** [twoshot.app/discord](https://twoshot.app/discord) - **GitHub Issues:** Report bugs and feature requests - **Rate Limits:** Check current limits in response headers **Need Help?** - Technical questions: developers@twoshot.app - Business inquiries: partnerships@twoshot.app - Bug reports: Create an issue on GitHub