Creator Onboarding
Endpoints for creator verification and payout setup, plus admin review. For the design and the PII rules behind it, see Creator Onboarding & KYC.
Creator endpoints
Mounted at /api/v1/creator-onboarding (apps/api/src/router/creatorOnboardingRoutes.ts). All require authentication. Writes are rate limited to 40 per hour per user.
| Method | Path | Body | Purpose |
|---|---|---|---|
GET | /creator-onboarding | — | Current status, used to resume the flow, on the review screen, and by the app's status card. Read-only: a user who never applied gets NOT_STARTED defaults and no profile row is created |
POST | /creator-onboarding/id-document | multipart: document (JPEG/PNG ≤ 10 MB), side, documentType | Upload one side of the government ID |
POST | /creator-onboarding/payout-account | { accountToken: "accttok_…" } | Create or update the Stripe Connect account |
POST | /creator-onboarding/bank-account | { bankToken: "btok_…" } | Attach the payout bank account |
POST | /creator-onboarding/submit | — | Hand off for review; sets User.onboarded |
documentType is one of license, state_id or passport. side is front or back; a passport has no back.
Status response
{
"success": true,
"data": {
"verificationStatus": "IN_PROGRESS",
"idDocumentType": "license",
"uploadedSides": ["front", "back"],
"steps": { "idDocument": true, "personalInfo": true, "bankAccount": false },
"bankLast4": null,
"bankName": null,
"payoutsEnabled": false,
"requirementsDue": [],
"rejectionReason": null,
"submittedAt": null
}
}verificationStatus moves NOT_STARTED → IN_PROGRESS → SUBMITTED → APPROVED | REJECTED. After a REJECTED, uploading a new ID puts it back to IN_PROGRESS.
Rules
- Tokens only. The payout and bank endpoints reject anything that isn't a Stripe token id, so raw PII can't be posted by mistake.
- Locked once submitted. ID uploads return
409whileSUBMITTEDorAPPROVED. Changing payout identity after approval also returns409and goes through support. - Submit is idempotent. Submitting again while
SUBMITTEDorAPPROVEDreturns the current status. If a step is missing, the response is400withcode: "ONBOARDING_INCOMPLETE". - Stripe errors are generic. Stripe's message can echo parameters, so it is never returned. Clients get a fixed message plus Stripe's error
code, and the server logs only the code and request id. 503means Stripe or the KYC bucket or key isn't configured. The routes fail closed.
Admin review
Mounted at /api/v1/admin/creator-verifications. Every route requires authentication andUser.role = "admin".
| Method | Path | Purpose |
|---|---|---|
GET | /admin/creator-verifications?status=SUBMITTED&limit=20&cursor= | Review queue, oldest submission first |
GET | /admin/creator-verifications/:userId | Profile, payout state, and ID document metadata |
GET | /admin/creator-verifications/:userId/documents/:assetId | Decrypted ID image. Audited, no-store |
POST | /admin/creator-verifications/:userId/approve | Approve; sets userType = "playpal" and the PlayPal badge, and notifies the user |
POST | /admin/creator-verifications/:userId/reject | { reason }. Reject, delete the ID images, and notify the user with the reason |
Every document view, approval and rejection writes a VerificationAccessLog row with the admin id, the subject, the asset and the request IP.
# Review queue
curl -H "Authorization: Bearer $ADMIN_JWT" \
https://api.playpalz.com/api/v1/admin/creator-verifications
# View a document (writes an audit row)
curl -H "Authorization: Bearer $ADMIN_JWT" -o /tmp/id-front.jpg \
https://api.playpalz.com/api/v1/admin/creator-verifications/<userId>/documents/<assetId>Treat downloaded IDs as live PII
Don't download ID images to shared machines or paste them into tickets or chat. Delete local copies after the review. The audit log records that you viewed the image, not where the copy went.
Stripe webhook
POST /api/v1/webhooks/stripe is covered in Webhooks → Stripe.
