Create a creator account (sign-up from your page)
Creates a creator (or agency) account from your own sign-up page and returns the ID-check link to redirect the creator to.
/api/external/v1/accountsalso served at /api/external/accountsNewCreates a creator (or agency) account from your own sign-up page and returns the ID-check link to redirect the creator to.
The account is created with everything our own onboarding sets — handle, display name, bio, residency, avatar, banner and subscription price — and starts as pending_kyc. Send the creator to kycUrl (a full-page redirect or a new tab, never an iframe). When they finish, they come back to your kycReturnUrl with ?kyc=approved|declined|in_review. That parameter is only a hint for your UI: the decision arrives as the account.kyc_updated webhook, and selling is unlocked only once the account is active.
The response also holds an apiKey for the new account, bound to your app. Use it straight away to upload media and create drops for the creator. Store it like a password; it is shown once. Your app owner’s own key can also act for the account under /api/external/v1/creators/{accountId}/… for as long as the account keeps that key.
Commission. Sales made through the links you get from GET /api/external/v1/links for the account (a drop bought, a tip, a subscription and all its renewals) earn your app owner a commission for life — 5% unless agreed otherwise, paid out of our platform fee, never taken from the creator.
Terms. Show the creator our Terms of Service, Privacy Policy and 2257 statement and record their acceptance. Send legalAccepted: true with the IP address and user agent of the creator’s browser at the moment they accepted (not your server’s). We generate the signed acceptance record from them.
Call this with a key your app owner holds: their own, or one their agency minted for its personas or managed creators. Creator sign-up is the Onboarding API category, off for new apps; ask us to switch it on (403 api_category_disabled until then).
Authentication
Send the creator's API key as a bearer token: Authorization: Bearer dpfn_…. See Authentication & API keys.
Request body
Content type: application/json
| Name | Type | Required | Description | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
email | string | Required | The creator’s email. It becomes their sign-in. 409 if it is already registered. Format: email | |||||||||
password | string | Optional | Optional. Leave it out and we email the creator a link to set one (valid 7 days). Either way they get a welcome email. Min length: 8 Max length: 200 | |||||||||
username | string | Required | Public @handle (letters, digits, underscores). 409 when taken. | |||||||||
displayName | string | Optional | Display name on the profile. Max length: 50 | |||||||||
bio | string | Optional | Profile bio. Max length: 500 | |||||||||
location | string | Optional | Optional profile location line. Max length: 100 | |||||||||
accountType | string | Optional | agency is for AI-creator operators that run several personas.One of: creator, agencyDefault: "creator" | |||||||||
residencyCountry | string | Required | Country of residence, ISO 3166-1 alpha-2. Used for payouts and tax. Pattern: ^[A-Z]{2}$ | |||||||||
avatarUrl | string | Optional | https URL of the profile picture (JPEG, PNG or WebP, ≤4 MB). Fetched once, stripped of metadata and moderated like a normal upload. A bad image never fails the sign-up; check images.avatar.Format: uri | |||||||||
bannerUrl | string | Optional | https URL of the profile banner. Same rules as avatarUrl. Format: uri | |||||||||
subscription | object | Optional | Optional subscription offer for the profile.Show child attributes
| |||||||||
legalAccepted | boolean | Required | Must be true: the creator accepted the Terms of Service, Privacy Policy and 2257 statement on your page. | |||||||||
acceptedFromIp | string | Required | The creator’s browser IP at acceptance. | |||||||||
acceptedUserAgent | string | Required | The creator’s browser user agent at acceptance. Max length: 500 | |||||||||
kycReturnUrl | string | Optional | https URL the creator returns to after the ID check. We append ?kyc=<status>.Format: uri |
{
"email": "valeria@example.com",
"username": "valeria",
"displayName": "Valeria",
"bio": "New sets every Friday.",
"residencyCountry": "SE",
"avatarUrl": "https://cdn.example.com/valeria/avatar.jpg",
"bannerUrl": "https://cdn.example.com/valeria/banner.jpg",
"subscription": {
"priceUsd": 9.99,
"interval": "monthly",
"trialDays": 0
},
"legalAccepted": true,
"acceptedFromIp": "203.0.113.7",
"acceptedUserAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) …",
"kycReturnUrl": "https://crm.example.com/onboarding/done"
}Responses
| Name | Type | Description | ||||||
|---|---|---|---|---|---|---|---|---|
accountId | string | The new account’s id. | ||||||
username | string | Public @handle (without @). | ||||||
displayName | string | null | Display name. | ||||||
email | string | null | Sign-in email. Format: email | ||||||
accountType | string | What the account was created as. One of: creator, agency | ||||||
status | string | pending_kyc until the ID check is approved, then active (can sell and get paid).One of: pending_kyc, active | ||||||
sellerStatus | string | Internal seller state, for support (PENDING_KYC, KYC_IN_REVIEW, KYC_REJECTED, VERIFIED_CREATOR, VERIFIED_AGENCY). | ||||||
kycStatus | string | ID-check state. session_created = link opened, not finished; processing = approved, compliance documents still being generated.One of: not_started, session_created, in_review, processing, approved, rejected | ||||||
profileUrl | string | Public profile URL. Format: uri | ||||||
createdAt | string | When the account was created. Format: date-time | ||||||
apiKey | string | A key for the new account, bound to your app. Shown once. | ||||||
kycUrl | string | null | The ID-check link. Null if it could not be opened right now; then call create-kyc-session. Format: uri | ||||||
kycExpiresAt | string | null | The link is reused until then; ask for a new one after. Format: date-time | ||||||
referralCredited | boolean | True when the account was attributed to your app owner. Commission does not depend on it: it is paid on sales through your app’s links. | ||||||
images | object | What happened to avatarUrl / bannerUrl: "saved", "failed" or null (not sent).Show child attributes
|
{
"accountId": "clxac0001acct",
"username": "valeria",
"displayName": "Valeria",
"email": "valeria@example.com",
"accountType": "creator",
"status": "pending_kyc",
"sellerStatus": "PENDING_KYC",
"kycStatus": "session_created",
"profileUrl": "https://www.dropfans.io/u/valeria",
"createdAt": "2026-10-17T16:00:00.000Z",
"apiKey": "dpfn_3f9c…",
"kycUrl": "https://verify.didit.me/session/…",
"kycExpiresAt": "2026-10-18T16:00:00.000Z",
"referralCredited": true,
"images": {
"avatar": "saved",
"banner": "saved"
}
}Errors
| Status | Body | When |
|---|---|---|
| 400 | {"error":"residencyCountry must be an ISO 3166-1 alpha-2 code (e.g. \"SE\").","code":"invalid_input","field":"residencyCountry"} | A field failed validation. `field` names it. |
| 409 | {"error":"There is already a DropFans account with this email. Sign in instead.","code":"email_taken"} | The email is registered. |
| 409 | {"error":"That profile link is taken. Pick another.","code":"username_taken"} | The handle is taken. |
| 401 | {"error":"Unauthorized","code":"unauthorized"} | Missing or invalid API key. |
| 403 | {"error":"This endpoint needs a key bound to an approved app.","code":"app_key_required"} | A Personal key was used. |
| 403 | {"error":"Use the app owner's key for this endpoint.","code":"owner_key_required"} | The key was minted by a creator for themselves. App-level calls need a key held by your app owner: their own, or one their agency minted for its personas or managed creators. |
| 429 | {"error":"At most 20 sign-ups per hour.","code":"rate_limited"} | Too many sign-ups from your app this hour. `Retry-After` says when to retry. |
| 400 | {"error":"This endpoint acts as the key’s own account or app — call it without /creators/{creatorId}.","code":"creator_scope_not_allowed"} | Called under /api/external/v1/creators/{creatorId}/ — this endpoint always acts as the key’s own account, agency or app. |
| 403 | {"error":"This app does not have the onboarding API category enabled. Contact support to enable it.","code":"api_category_disabled","category":"onboarding"} | Your app has the Creator onboarding [API category](/developers/concepts/api-categories) switched off (the default for new apps). |
Rate limiting
Every response carries the X-RateLimit-Tier header and, on limited tiers, the per-minute and per-day trios — read X-RateLimit-Remaining and X-RateLimit-Reset instead of hardcoding limits. Details in Rate limits.
Code samples
curl -X POST "https://www.dropfans.io/api/external/v1/accounts" \
-H "Authorization: Bearer $DROPFANS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "valeria@example.com",
"username": "valeria",
"displayName": "Valeria",
"bio": "New sets every Friday.",
"residencyCountry": "SE",
"avatarUrl": "https://cdn.example.com/valeria/avatar.jpg",
"bannerUrl": "https://cdn.example.com/valeria/banner.jpg",
"subscription": {
"priceUsd": 9.99,
"interval": "monthly",
"trialDays": 0
},
"legalAccepted": true,
"acceptedFromIp": "203.0.113.7",
"acceptedUserAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) …",
"kycReturnUrl": "https://crm.example.com/onboarding/done"
}'const res = await fetch(`https://www.dropfans.io/api/external/v1/accounts`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"email": "valeria@example.com",
"username": "valeria",
"displayName": "Valeria",
"bio": "New sets every Friday.",
"residencyCountry": "SE",
"avatarUrl": "https://cdn.example.com/valeria/avatar.jpg",
"bannerUrl": "https://cdn.example.com/valeria/banner.jpg",
"subscription": {
"priceUsd": 9.99,
"interval": "monthly",
"trialDays": 0
},
"legalAccepted": true,
"acceptedFromIp": "203.0.113.7",
"acceptedUserAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) …",
"kycReturnUrl": "https://crm.example.com/onboarding/done"
}),
});
console.log(await res.json());import os
import requests
res = requests.post(
"https://www.dropfans.io/api/external/v1/accounts",
headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
json={
"email": "valeria@example.com",
"username": "valeria",
"displayName": "Valeria",
"bio": "New sets every Friday.",
"residencyCountry": "SE",
"avatarUrl": "https://cdn.example.com/valeria/avatar.jpg",
"bannerUrl": "https://cdn.example.com/valeria/banner.jpg",
"subscription": {
"priceUsd": 9.99,
"interval": "monthly",
"trialDays": 0,
},
"legalAccepted": True,
"acceptedFromIp": "203.0.113.7",
"acceptedUserAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) …",
"kycReturnUrl": "https://crm.example.com/onboarding/done",
},
)
print(res.json())Questions? support@dropfans.io
