Last active
June 12, 2025 04:00
-
-
Save tobq/02c904192e1a76fca5dc6df445fa7726 to your computer and use it in GitHub Desktop.
Revisions
-
tobq revised this gist
Jun 12, 2025 . 1 changed file with 0 additions and 53 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -73,59 +73,6 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... X-API-Key: your-api-key-here ``` --- ## Project Management -
tobq created this gist
Jun 12, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,935 @@ # 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 ``` #### 3. OAuth2 Authorization Code Flow ```http # Redirect users to authorization URL https://api.twoshot.app/oauth/authorize?client_id=YOUR_CLIENT&response_type=code&scope=read write # Exchange code for tokens POST /auth/exchange Content-Type: application/json { "code": "authorization_code", "client_id": "your_client_id", "client_secret": "your_client_secret" } ``` **Scopes:** - `read` - Access user data and public content - `write` - Create and modify user content ### Authentication Flow Examples #### User Registration & Login ```javascript // Register new user const registerResponse = await fetch('/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ auth0IdToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", userName: "musicmaker2024", firstName: "John", lastName: "Doe" }) }); const tokens = await registerResponse.json(); // Store tokens.accessToken and tokens.refreshToken ``` #### Token Refresh ```javascript // Refresh expired token const refreshResponse = await fetch('/auth/refresh', { method: 'POST', headers: { 'Authorization': `Bearer ${refreshToken}` } }); const newTokens = await refreshResponse.json(); ``` --- ## 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": "[email protected]", "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 <details> <summary>Click to expand all 61 public endpoints</summary> ### 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... </details> --- ## 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: [email protected] - Business inquiries: [email protected] - Bug reports: Create an issue on GitHub