Spaces:
Sleeping
Sleeping
| You’re seeing a 422 on `POST /api/v1/invitations`, and you want to confirm whether the frontend is sending the right payload from `CreateOrganizationModal` so you can decide if the bug is backend-side. I’ll pull up that component, inspect how it builds the request body, and then describe the exact JSON shape it sends. | |
| I'll open `CreateOrganizationModal.tsx` to see exactly how it constructs the payload for the invitations endpoint, then I'll summarize the JSON shape it sends so you can compare with the backend expectations. | |
| I'll extract the invitation payload construction from [CreateOrganizationModal](cci:1://file:///d:/atomio/swiftops-frontend/src/components/dashboard/platform-admin/CreateOrganizationModal.tsx:25:0-395:1) and describe the exact JSON shape sent to `POST /api/v1/invitations`, including field names and possible values/nullability, for both Telecom operator and Contractor flows. Then you can compare that with what the backend validator expects. | |
| Here’s the exact JSON body that [CreateOrganizationModal](cci:1://file:///d:/atomio/swiftops-frontend/src/components/dashboard/platform-admin/CreateOrganizationModal.tsx:25:0-395:1) sends to `POST /api/v1/invitations`, and how it varies between a Telecom operator and a Contractor. This should let you compare directly with your backend schema to see why you’re getting a 422. | |
| ## Shape of the `POST /api/v1/invitations` request | |
| This is built in [CreateOrganizationModal.handleSubmit](cci:1://file:///d:/atomio/swiftops-frontend/src/components/dashboard/platform-admin/CreateOrganizationModal.tsx:74:2-184:4) around lines 151–166. | |
| When `inviteAdmin` is enabled and after the org is created, the component builds: | |
| ```ts | |
| const invitedRole = orgType === 'Telecom operator' ? 'client_admin' : 'contractor_admin'; | |
| const invitationsPayload: any = { | |
| email: adminEmail.trim(), | |
| phone: adminPhone.trim() || undefined, | |
| invited_role: invitedRole, | |
| client_id: orgType === 'Telecom operator' ? createdOrg.id : null, | |
| contractor_id: orgType === 'Contractor' ? createdOrg.id : null, | |
| invitation_method: invitationMethod, | |
| }; | |
| ``` | |
| Then sends: | |
| ```ts | |
| fetch(`${API_BASE}/invitations`, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify(invitationsPayload), | |
| }); | |
| ``` | |
| ### Final JSON body (frontend side) | |
| **Common fields:** | |
| - `email`: `string` | |
| - Value: `adminEmail` from the form, trimmed. | |
| - Required by the frontend: if `inviteAdmin` is on and this is empty, it shows: “Admin email is required when inviting an admin” and does **not** call the API. | |
| - `phone`: `string | undefined` | |
| - Value: trimmed `adminPhone`, or omitted (becomes `undefined` → dropped by `JSON.stringify`) if empty. | |
| - `invited_role`: `"client_admin" | "contractor_admin"` | |
| - `"client_admin"` if `orgType === 'Telecom operator'` | |
| - `"contractor_admin"` if `orgType === 'Contractor'` | |
| - `client_id`: `string | null` | |
| - `createdOrg.id` if `orgType === 'Telecom operator'` | |
| - `null` if `orgType === 'Contractor'` | |
| - `contractor_id`: `string | null` | |
| - `createdOrg.id` if `orgType === 'Contractor'` | |
| - `null` if `orgType === 'Telecom operator'` | |
| - `invitation_method`: `"whatsapp" | "email" | "both"` | |
| - Default is `"whatsapp"`; user can change it in the select. | |
| **Notably *not* sent:** | |
| Even though the UI captures these, they are **not included** in the payload: | |
| - `adminFirstName` | |
| - `adminLastName` | |
| So the backend must be okay with only `email`, `phone?`, `invited_role`, `client_id`, `contractor_id`, and `invitation_method`. | |
| ### Example payloads | |
| **Telecom operator org:** | |
| ```json | |
| { | |
| "email": "admin@example.com", | |
| "phone": "+254700000000", | |
| "invited_role": "client_admin", | |
| "client_id": "<newly-created-client-id>", | |
| "contractor_id": null, | |
| "invitation_method": "whatsapp" | |
| } | |
| ``` | |
| **Contractor org:** | |
| ```json | |
| { | |
| "email": "admin@example.com", | |
| "phone": "+254700000000", | |
| "invited_role": "contractor_admin", | |
| "client_id": null, | |
| "contractor_id": "<newly-created-contractor-id>", | |
| "invitation_method": "whatsapp" | |
| } | |
| ``` | |
| If `phone` is left blank, that key is omitted completely. | |
| --- | |
| If you paste your backend `InvitationCreate`/Pydantic schema (or similar) I can line‑by‑line compare and point out the exact mismatch likely causing the 422. |