User
Set User Intent
One-time, post-registration choice — FAN or CREATOR. Drives the post-signup landing destination and is rejected on second call.
PATCH /api/v1/users/intent — 🔑 Bearer
Sets User.intent to FAN or CREATOR. One-time only — after the value is set, all subsequent calls return 400 intent_already_set. The atomic updateMany({ intent: null }) guard prevents a race between two concurrent requests.
After this succeeds, route the user accordingly: FAN → discovery feed, CREATOR → onboarding wizard. The flag never changes after the first set; account-type changes are out of scope for this endpoint.
Request
Body — SetIntentDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
intent | enum (UserIntent) | ✓ | IsEnum(UserIntent) | One of: FAN / CREATOR |
| Header | Required | Notes |
|---|---|---|
Authorization: Bearer <accessToken> | ✓ | JWT from POST /auth/login |
Response
200 OK — SuccessOnlyResponseDto
{
"success": true
}| Field | Type | Notes |
|---|---|---|
success | boolean | Always true on 200 |
Errors
| HTTP | code / i18nKey | Reason |
|---|---|---|
400 | (DTO validation) | intent not in the enum |
400 | error.user.intent_already_set | User.intent is non-null — second call after the value was already set |
401 | (guard) | Missing / invalid bearer token |
404 | error.user.not_found | Token decoded but user row missing |
Side effects
- Atomic check-and-set:
prisma.user.updateMany({ where: { id, intent: null }, data: { intent } }). If the row already had a non-null intent,count = 0and no write happens. - If
count = 0, look up the user to distinguish "intent already set" from "user not found", then throw the matching error. - On success: increment
userIntentSetTotalPrometheus counter (labelled by chosen intent). - Audit log:
[intent] User {userId} set intent: {intent}.
Code samples
curl -X PATCH https://api.bio.re/api/v1/users/intent \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"intent": "CREATOR"}'type UserIntent = 'FAN' | 'CREATOR';
async function setIntent(accessToken: string, intent: UserIntent): Promise<void> {
const res = await fetch('https://api.bio.re/api/v1/users/intent', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ intent }),
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Set intent failed'), {
code: json?.error?.code,
});
}
}import { useMutation, useQueryClient } from '@tanstack/react-query';
export function useSetIntent() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (intent: UserIntent) => {
const res = await fetch('/api/v1/users/intent', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ intent }),
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Set intent failed'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
},
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['users', 'profile'] });
qc.invalidateQueries({ queryKey: ['auth', 'me'] });
},
});
}Try it
Authorization
bearer AuthorizationBearer <token>
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
curl -X PATCH "https://loading/api/v1/users/intent" \ -H "Content-Type: application/json" \ -d '{ "intent": "FAN" }'{
"success": true
}{
"success": false,
"error": {
"code": "AUTH_UNAUTHORIZED",
"message": "Invalid credentials",
"i18nKey": "auth.login.invalid_credentials",
"i18nVars": {
"field": "email"
},
"details": [
{
"message": "email must be an email"
}
],
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}{
"success": false,
"error": {
"code": "AUTH_UNAUTHORIZED",
"message": "Invalid credentials",
"i18nKey": "auth.login.invalid_credentials",
"i18nVars": {
"field": "email"
},
"details": [
{
"message": "email must be an email"
}
],
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Source
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/user/user.controller.ts | 92–99 (setIntent) |
| DTO (request) | apps/api-core/src/modules/user/dto/index.ts | 45–49 (SetIntentDto) |
| DTO (response) | apps/api-core/src/common/dto/common-response.dto.ts | SuccessOnlyResponseDto |
| Service | apps/api-core/src/modules/user/user.service.ts | 223–241 (setIntent) |
| Prisma enum | packages/prisma/prisma/schema.prisma | enum UserIntent (FAN / CREATOR), User.intent |