{"openapi":"3.1.0","info":{"title":"Nightshift API","version":"1.0.0","description":"Make, list and publish videos, and generate images, video and audio, programmatically. Videos are prepaid in US dollars, separate from site credits; generations (/creative) are paid in the organization's credits, exactly like the Studio. A video is priced per minute of requested length, with a minimum per video, from the live price list on /docs/api#pricing; every POST /videos answer carries its price_cents."},"servers":[{"url":"https://nightshift-ai.studio/api/v1"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"An API key: nsk_live_ followed by 43 characters. A key has scopes (account:read, videos:read, videos:write, creative:quote, creative:create, creative:read); each operation names the one it needs in x-required-scope, and a key without it is refused (403). Keys made before generations existed hold only account:read, videos:read, videos:write. A key may also carry its own requests-per-minute limit (it can only lower the usage tier's, up to 300) and a monthly credit ceiling for generations."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Retry-safe POST: the same key and body within 24 hours replays the first success.","schema":{"type":"string","pattern":"^[A-Za-z0-9_:.-]{1,255}$"}},"IdempotencyKeyRequired":{"name":"Idempotency-Key","in":"header","required":true,"description":"Required: a generation spends credits, so a retry must not spend twice. The same key and body replays the first answer; the same key with another body is 422 idempotency_key_reused. Keys are per API key.","schema":{"type":"string","pattern":"^[A-Za-z0-9_:.-]{1,255}$"}}},"headers":{"RequestId":{"schema":{"type":"string"},"description":"Quote it when contacting support."},"RateLimitLimit":{"schema":{"type":"integer"}},"RateLimitRemaining":{"schema":{"type":"integer"}},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until the minute window resets."},"RetryAfter":{"schema":{"type":"integer"}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","code","message","request_id"],"properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","billing_error","permission_error","not_found_error","conflict_error","idempotency_error","rate_limit_error","api_error"]},"code":{"type":"string","enum":["invalid_api_key","api_not_activated","key_owner_not_admin","rate_limit_exceeded","concurrency_limit_exceeded","insufficient_balance","monthly_limit_reached","key_limit_reached","insufficient_scope","insufficient_credits","key_credit_limit_reached","run_limit_reached","price_changed","max_credits_required","idempotency_key_required","model_not_sellable","entitlement_required","unpriced","capability_not_supported","source_unavailable","style_unavailable","mode_not_supported","route_changed","no_model_available","registry_missing","forbidden","invalid_body","unknown_parameter","invalid_params","channel_required","duration_required","channel_not_found","channel_not_active","video_not_found","job_not_found","download_not_found","no_master","targets_required","too_many_targets","publish_refused","invalid_idempotency_key","idempotency_key_reused","idempotency_in_progress","queue_backend_required","downloads_unavailable","pricing_unavailable","api_unavailable","upstream_error","internal_error","unknown_endpoint"]},"message":{"type":"string"},"request_id":{"type":"string"},"retry_after":{"type":"integer"}},"additionalProperties":true}}},"CreateVideo":{"type":"object","required":["channel_id"],"additionalProperties":false,"properties":{"channel_id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,63}$"},"topic":{"type":"string","maxLength":300},"niche":{"type":"string","maxLength":120},"duration":{"type":"integer","minimum":30,"maximum":3600,"description":"Seconds. Defaults to the channel's target length; the price is based on it."},"language":{"type":"string","maxLength":40},"visual_style":{"type":"string","maxLength":300}}},"Job":{"type":"object","properties":{"id":{"type":"integer"},"channel_id":{"type":"string"},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled"]},"params":{"type":"object"},"attempts":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"],"description":"One plain sentence saying why a job failed; never a log."},"error_code":{"type":["string","null"],"enum":["credits","voice","provider_quota","media","render","timeout","unknown",null],"description":"Why a job failed, as a stable code."},"charge":{"type":["object","null"],"properties":{"status":{"type":"string","enum":["open","captured","released"]},"held_cents":{"type":"integer"},"captured_cents":{"type":["integer","null"]}}}}},"Video":{"type":"object","properties":{"id":{"type":"string"},"channel_id":{"type":"string"},"title":{"type":["string","null"]},"topic":{"type":["string","null"]},"format":{"type":["string","null"]},"published_at":{"type":["string","null"]},"privacy":{"type":["string","null"]},"publish_state":{"type":["string","null"]},"review_state":{"type":["string","null"]},"youtube_url":{"type":["string","null"]}}},"Publish":{"type":"object","additionalProperties":false,"properties":{"account_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Connected Instagram / TikTok accounts."},"channel_ids":{"type":"array","items":{"type":"string"},"description":"The organization's YouTube channels (uploaded private)."}}},"Balance":{"type":"object","properties":{"currency":{"const":"usd"},"exempt":{"type":"boolean"},"balance_cents":{"type":"integer"},"reserved_cents":{"type":"integer"},"available_cents":{"type":"integer"},"month_spend_cents":{"type":"integer"},"monthly_limit_cents":{"type":["integer","null"]},"tier":{"type":"integer","enum":[0,1,2,3,4]}}},"CreativeQuoteRequest":{"type":"object","required":["capability","params"],"additionalProperties":false,"properties":{"capability":{"type":"string","enum":["t2i","t2v","tts","sfx","music","edit","i2v","upscale","remove_bg","voice_change","dub","video_upscale","describe","captions","expand","relight","erase","video_edit","video_reframe","video_remove_bg"]},"model":{"type":"string","description":"A model id the API sells for this capability (not every model the Studio offers is on the API). Required for mode exact; not read for the other modes."},"params":{"type":"object","additionalProperties":false,"description":"What to generate. edit, i2v, upscale, remove_bg and describe take source_asset_id: an image in the key's organization's media library. A value the model does not offer (for example an image quality tier it does not list) is refused with 400 invalid_params, or refused (422) when it has no price, before anything is charged.","properties":{"prompt":{},"negative_prompt":{},"aspect_ratio":{},"resolution":{},"duration_s":{},"voice_id":{},"seed":{},"source_asset_id":{},"factor":{},"style_kit_id":{},"target_language":{},"target_resolution":{},"end_asset_id":{},"language":{},"quality":{},"audio":{},"look":{}}},"mode":{"type":"string","enum":["exact","auto","cheap","fast","quality"],"default":"exact","description":"exact: the model you named runs, never another. auto / cheap / fast / quality: the API picks among the models it sells that are available, priced for these params and allowed by the plan (auto: the lowest price of the higher quality tiers; cheap: the lowest price; fast: the fastest; quality: the highest quality tier). The quote names the pick (routed_model) and why (route_reason)."}}},"CreativeCreateRequest":{"type":"object","required":["capability","model","params","max_credits"],"additionalProperties":false,"properties":{"capability":{"type":"string","enum":["t2i","t2v","tts","sfx","music","edit","i2v","upscale","remove_bg","voice_change","dub","video_upscale","describe","captions","expand","relight","erase","video_edit","video_reframe","video_remove_bg"]},"model":{"type":"string","description":"For mode exact, the model; for the other modes, the routed_model the quote named."},"params":{"type":"object","description":"As in the quote."},"mode":{"type":"string","enum":["exact","auto","cheap","fast","quality"],"default":"exact"},"max_credits":{"type":"number","minimum":0,"description":"The most credits you accept to be charged. A price above it is refused (409) and nothing is charged. You are never charged more than the price quoted when the job was started, whatever max_credits says. For a routed mode a failover goes only to a compatible model costing no more than that quote, and the job is charged the price of the model that made it. A different pick than the quote's is refused (409): quote again."}}},"CreativeQuote":{"type":"object","properties":{"quote":{"type":"object","properties":{"credits":{"type":"number","description":"What the generation costs; exactly what you are charged if it succeeds."},"exempt":{"type":"boolean"},"model":{"type":"string"},"capability":{"type":"string"},"unit":{"type":"string"},"quantity":{"type":"number"},"credits_per_unit":{"type":"number","description":"Credits per unit of quantity, as charged."},"minimum":{"type":"number"},"mode":{"type":"string","description":"Routed modes only."},"routed_model":{"type":"string","description":"Routed modes only: the model the API picked; send it back as model."},"route_reason":{"type":"string","enum":["cheapest","fastest","best_quality","best_value","best_available","only_option"],"description":"Routed modes only: why that model was picked."}}}}},"CreativeJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"capability":{"type":"string"},"model":{"type":"string"},"status":{"type":"string","enum":["queued","running","provider_pending","processing","completed","failed","cancelled","expired"]},"quoted_credits":{"type":"number","description":"The price quoted when the job was started."},"charged_credits":{"type":["number","null"],"description":"What was captured; null until the job ends. 0 when it failed, was cancelled or expired (nothing is charged)."},"error_code":{"type":["string","null"]},"error":{"type":["string","null"]},"result":{"type":["object","null"],"description":"Files (type, size, digest) or, for describe, the text. Outputs are in the organization's media library: result_asset_ids."},"result_asset_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":"string","format":"date-time","description":"A job that has not started by then expires and nothing is charged."},"mode":{"type":"string","enum":["exact","auto","cheap","fast","quality"]},"routed_model":{"type":"string","description":"The model that runs the job: the model you named (exact) or the quoted pick."},"fallback_from":{"type":["string","null"],"description":"Routed modes: the model the job moved away from when it was unavailable."},"fallback_reason":{"type":["string","null"],"enum":["unavailable",null],"description":"Routed modes: set when the job moved to another model; always unavailable."},"route_reason":{"type":["string","null"],"description":"Routed modes: why the model was picked."}}},"Download":{"type":"object","properties":{"id":{"type":"integer"},"video_id":{"type":"string"},"quality":{"type":"string","enum":["720p","1080p"]},"status":{"type":"string","enum":["queued","processing","ready","failed","expired"]},"file_url":{"type":"string"},"expires_at":{"type":["string","null"],"format":"date-time"}}}},"responses":{"E400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E402":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E403":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E410":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E422":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}},"E503":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}}}}},"paths":{"/me":{"get":{"summary":"The key's organization, usage tier and limits","responses":{"200":{"description":"OK"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"none (any valid key)"}},"/balance":{"get":{"summary":"Prepaid API balance and this month's spend","description":"Top up in the Developer console (5–5000 USD per payment).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"account:read"}},"/channels":{"get":{"summary":"The organization's channels","responses":{"200":{"description":"OK"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"account:read"}},"/accounts":{"get":{"summary":"Publish targets: YouTube channels and connected Instagram / TikTok accounts","responses":{"200":{"description":"OK"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"account:read"}},"/videos":{"post":{"summary":"Make a video (queued; charged when the job succeeds)","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVideo"}}}},"responses":{"201":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"job_id":{"type":"integer"},"channel_id":{"type":"string"},"status":{"const":"queued"},"price_cents":{"type":["integer","null"]}}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429"},"503":{"$ref":"#/components/responses/E503"}},"x-required-scope":"videos:write"},"get":{"summary":"List videos, newest first","parameters":[{"name":"channel_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":10000,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"videos":{"type":"array","items":{"$ref":"#/components/schemas/Video"}}}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"videos:read"}},"/videos/{id}":{"get":{"summary":"One video with its publish requests","parameters":[{"name":"id","in":"path","required":true,"description":"Video id","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Video"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"videos:read"}},"/videos/{id}/publish":{"post":{"summary":"Cross-post a finished video (free). Same gate and approvals as the site; YouTube uploads are private.","parameters":[{"name":"id","in":"path","required":true,"description":"Video id","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Publish"}}}},"responses":{"200":{"description":"One request per target, or why it was refused"},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"videos:write"}},"/videos/{id}/downloads":{"post":{"summary":"Order a 720p / 1080p download (priced from the live list at /docs/api#pricing, charged when ready)","parameters":[{"name":"id","in":"path","required":true,"description":"Video id","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["quality"],"properties":{"quality":{"enum":["720p","1080p"]}}}}}},"responses":{"200":{"description":"An existing download reused"},"201":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Download"}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429"},"503":{"$ref":"#/components/responses/E503"}},"x-required-scope":"videos:write"}},"/downloads/{id}":{"get":{"summary":"A download's status","parameters":[{"name":"id","in":"path","required":true,"description":"Download id","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Download"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"videos:read"}},"/downloads/{id}/file":{"get":{"summary":"The MP4 of a ready download","parameters":[{"name":"id","in":"path","required":true,"description":"Download id","schema":{"type":"string"}}],"responses":{"200":{"description":"video/mp4","content":{"video/mp4":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"410":{"$ref":"#/components/responses/E410"},"429":{"$ref":"#/components/responses/E429"},"503":{"$ref":"#/components/responses/E503"}},"x-required-scope":"videos:read"}},"/creative/quote":{"post":{"summary":"The price of one generation, in credits. Nothing is charged.","x-required-scope":"creative:quote","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreativeQuoteRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreativeQuote"}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429"},"503":{"$ref":"#/components/responses/E503"}}}},"/creative/jobs":{"post":{"summary":"Start a generation: priced at the quote, charged when it succeeds, nothing charged if it fails.","description":"The same start as the Studio's, on the organization's credits: you are charged the quote when the generation succeeds, and nothing if it fails, expires or is cancelled. Idempotency-Key and max_credits are required. The plan's limit on generations running at once applies (429).","x-required-scope":"creative:create","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyRequired"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreativeCreateRequest"}}}},"responses":{"200":{"description":"The job's own idempotency record answered: the same job, nothing more charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreativeJob"}}}},"201":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreativeJob"}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"409":{"$ref":"#/components/responses/E409"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429"},"503":{"$ref":"#/components/responses/E503"}}}},"/creative/jobs/{id}":{"get":{"summary":"A generation this key started: status, charged credits, result.","description":"A generation this key did not start, or that does not exist, answers 404 job_not_found.","x-required-scope":"creative:read","parameters":[{"name":"id","in":"path","required":true,"description":"Generation id","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreativeJob"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/jobs/{id}":{"get":{"summary":"A video job's status and charge","parameters":[{"name":"id","in":"path","required":true,"description":"Job id","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}},"x-required-scope":"videos:read"}}}}