Vault for integrations
The vault is the creator's media library: every file the creator has, whether it is part of a product or not. This guide walks a CRM or automation through the whole cycle with a generated API key - list and filter, upload, classify, declare consent, wait for moderation, build products from vault files, and take products apart again - so the creator does not have to open the Fangate dashboard for day-to-day work.
Prerequisites: a generated API key with Manage content (see API Keys). Read-only keys can do the reads on this page, nothing else.
1. List and filter
GET /api/media - paginated, newest first by default. Rate limit: 60 requests per minute per key and IP, plus 120 per minute per IP across all keys behind it (shared with GET /api/media/{media}).
| Parameter | Type | Meaning |
|---|---|---|
page, limit | integer | Pagination (limit up to 100). |
status[] | pending, approved, rejected | Moderation state. Scalars work too (status=approved). |
media_type[] | image, video | Kind of file. |
consent[] | required, pending, provided | Consent bucket: required = the owner has to act (nobody tagged yet, or declined, or rescinded); pending = requested, no answer yet; provided = nothing to clear (not_required) or consent given. Read consent.status per file for the exact state. |
folder_id | integer | Vault folder (folders with scope=vault, see Content Folders). |
unlinked | boolean | Only files that belong to no product. |
live | boolean | Approved by moderation and consent cleared - the files a product can be built from. |
standard_only / ai_only | boolean | Only non-AI files / only AI files. |
sort | newest, oldest | |
search | string | Title search, up to 120 characters. |
The response is a page: data[], total, per_page, current_page, pages_total. Each file carries status, ai_origin, ai_label, is_should_consent, consent { status, required, fulfilled }, product_ids, is_linked, thumbnail, preview, preview_blurred. source (the full-size file) is only returned by GET /api/media/{media}.
live=1 is necessary, not sufficient, for a sale: a product built from live files can still be blocked by a pending manual review, an open price review or a switched-off AI domain. Always read is_accessible and purchase_block_code on the product.
2. Upload into the vault
POST /api/upload-sessions-session_type: device_multipart,file_name,file_size_bytes,mime_type, and the AI answerai_origin(+ai_attestedfor AI values). See AI content for the four values.- Upload the bytes as instructed (
initial_uploadfor single-part,POST /api/upload-sessions/{id}/partsfor multipart), thenPOST /api/upload-sessions/{id}/complete. - Poll
GET /api/upload-sessions/{id}untilstatusisready. Presigned URLs are valid for 15 minutes, a session for 24 hours. POST /api/mediawithupload_session_id, optionaltitle,description,folder_id,is_should_consent, andai_origin/ai_attestedif you did not send them on the session.
Several files at once: POST /api/media/bulk opens up to 50 sessions with shared defaults (including defaults.ai_origin and defaults.ai_attested); then run steps 2-4 per session.
The AI answer is mandatory for keys created since the AI release and for all content keys after the announced cut-over: without it, step 4 is refused with 422 and errors_code: ai_origin_required. The session stays ready - repeat the call with the value within the session's 24 hours; an asset that is never attached is cleaned up after 7 days.
3. Consent per file
A file that shows another person needs that person's consent before it can be sold. Declare it on the file, not on the product:
- Tag registered creators:
POST /api/media/{media}/consent-tagswithdate(the consent date) andtagged_creator_ids[](or a singletagged_creator_id). Find ids withGET /api/creators/search?q=. - Tag or invite by email: same call with
email. A registered address is tagged directly; an unknown address needstag_invite: true(and an optionalmessage) - Fangate emails an invitation and the file waits for their answer. An unknown address withouttag_inviteis refused with400. The call answers200with an emptydata; read the state back withGET. - Mark now, tag later:
PATCH /api/media/{media}withis_should_consent: trueblocks the file until a person is tagged and has accepted. - Read the state:
GET /api/media/{media}/consent-tags, or theconsentobject on every listing.
consent.status is one of not_required, review_open, consent_requested, consent_provided, declined, consent_rescinded; the list filter consent[] groups them into required (review_open, declined, consent_rescinded - the owner has to act), pending (consent_requested) and provided (not_required and consent_provided).
An AI file of another person (ai_origin: real_person_other) is marked as needing consent automatically.
4. Wait for moderation
Moderation runs after step 4 and takes seconds to minutes (videos longer; a first upload of a high-risk account waits for a manual review). There is no webhook for moderation or consent events today - poll the list (GET /api/media?status[]=pending) with a backoff of at least 30 seconds rather than polling every file; the list and the single-file endpoint share a limit of 60 requests per minute per key. Outbound webhooks exist for payments only (payment.successful, payment.failed, payment.pending).
5. Build a product from vault files
POST /api/products with
| Field | Notes |
|---|---|
media_ids[] | Vault files of the creator. Mixing AI and non-AI files makes the product an AI product (see AI content). A file with ai_classified: false (not classified yet, reported as none) counts as non-AI until it is classified; the product flag follows then. |
price | Cents, at least 500. |
title | Optional. |
is_downloadable | Send it explicitly. Absent means false on this call. |
is_adult_content, is_verif_age | Default from the account's upload defaults. |
per_media_consent, media_consents[], consent_date | Declare consent for specific files in the same call: "per_media_consent": true, "consent_date": "2026-09-12", "media_consents": [{ "media_id": 101, "tagged_creator_ids": [55] }]. Without per_media_consent: true the media_consents list is silently ignored. |
No ai_origin here: the files are classified already. The response is the full product resource (201), with link, is_accessible and purchase_block_code.
Add or remove files later with POST /api/products/{product}/media (media_ids[], optionally with the consent fields above) and DELETE /api/products/{product}/media/{media}. Removing the last file deletes the product; in that case data is the string "deleted" instead of the product object.
6. Take a product apart
DELETE /api/products/{product} removes the product and, by default, its files from the vault as long as no other product uses them. To keep the files, send delete_media=0:
DELETE /api/products/123?delete_media=0DELETE /api/media/{media} deletes a vault file; products that only had this file are deleted with it, other products lose the file.
7. Is it sellable?
Read GET /api/products/{product}:
is_accessible | purchase_block_code | Meaning |
|---|---|---|
true | null | Buyers can purchase. |
false | moderation_not_approved | No file of the product is approved yet. One approved file is enough for the sale; a rejected file next to it does not block - check status per file in GET /api/media if every file must be through. |
false | manual_review_pending | Waiting for a manual review (high-risk accounts). |
false | consent_recipient_missing, consent_pending, consent_declined, consent_rescinded | Consent workflow open or negative. |
false | price_review_pending | The price is above the threshold and awaits approval by Fangate. |
false | media_unavailable | A file is not available in storage. |
false | ai_rail_unavailable | AI product while Fangate's platform-wide AI switch is off. |