{"openapi":"3.0.0","paths":{"/auth/login":{"post":{"operationId":"AuthController_loginRoute","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginDto"}}}},"responses":{"200":{"description":"Login successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Invalid credentials"},"429":{"description":"Rate limit exceeded — try again later"}},"summary":"Authenticate and obtain a JWT access token","tags":["auth"]}},"/auth/register":{"post":{"operationId":"AuthController_registerRoute","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDto"}}}},"responses":{"201":{"description":"User registered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResponseDto"}}}},"400":{"description":"Invalid request body"},"409":{"description":"Email already registered"},"429":{"description":"Rate limit exceeded — try again later"}},"summary":"Register a new user account (auto-login returns JWT)","tags":["auth"]}},"/auth/onboarding":{"post":{"operationId":"AuthController_onboardingRoute","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingDto"}}}},"responses":{"200":{"description":"Perfil completado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingResponseDto"}}}},"400":{"description":"Falta el teléfono o el consentimiento"},"401":{"description":"Token inválido o ausente"}},"security":[{"bearer":[]}],"summary":"Completar el alta: teléfono + aceptación de términos y privacidad (usado tras el login social)","tags":["auth"]}},"/auth/me":{"get":{"operationId":"AuthController_me","parameters":[],"responses":{"200":{"description":"Authenticated user info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponseDto"}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"Get the authenticated user profile","tags":["auth"]},"patch":{"operationId":"AuthController_updateMe","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileDto"}}}},"responses":{"200":{"description":"Perfil actualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponseDto"}}}},"401":{"description":"Token inválido o ausente"}},"security":[{"bearer":[]}],"summary":"Actualizar teléfono y/o nombre del perfil autenticado","tags":["auth"]}},"/auth/trusted/login-by-phone":{"post":{"operationId":"TrustedAuthController_loginByPhoneRoute","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginByPhoneDto"}}}},"responses":{"200":{"description":"Login correcto","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustedAuthResponseDto"}}}},"401":{"description":"Falta o es inválida la API key"},"403":{"description":"La Service Account no tiene el permiso auth:trusted_phone_login"},"404":{"description":"No existe usuario con ese teléfono"},"429":{"description":"Límite de peticiones excedido"}},"security":[{"api-key":[]}],"summary":"Emitir un JWT de usuario a partir de su teléfono verificado (bot de confianza)","tags":["auth"]}},"/auth/trusted/register-by-phone":{"post":{"operationId":"TrustedAuthController_registerByPhoneRoute","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterByPhoneDto"}}}},"responses":{"201":{"description":"Usuario creado (auto-login)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustedAuthResponseDto"}}}},"400":{"description":"Falta la aceptación de términos/privacidad o un campo inválido"},"401":{"description":"Falta o es inválida la API key"},"403":{"description":"La Service Account no tiene el permiso auth:trusted_phone_login"},"409":{"description":"Ya existe una cuenta con ese email"},"429":{"description":"Límite de peticiones excedido"}},"security":[{"api-key":[]}],"summary":"Alta passwordless de un usuario por teléfono verificado (bot de confianza)","tags":["auth"]}},"/grants/at-scope":{"get":{"operationId":"GrantsController_listAtScope","parameters":[{"name":"scopeType","required":true,"in":"query","description":"platform | organization | emergency | group | entity","schema":{"type":"string"}},{"name":"scopeId","required":false,"in":"query","schema":{"type":"string"}},{"name":"scopeEntityType","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GrantListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not authorized to administer this scope"}},"security":[{"bearer":[]}],"summary":"List the grants made AT a scope (scoped administrator)","tags":["grants"]}},"/grants":{"get":{"operationId":"GrantsController_list","parameters":[{"name":"principalId","required":true,"in":"query","description":"User or service-account id","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GrantListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"}},"security":[{"bearer":[]}],"summary":"List the grants held by a principal (admin)","tags":["grants"]},"post":{"operationId":"GrantsController_grant","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRoleDto"}}}},"responses":{"201":{"description":"Role granted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not authorized to grant, or privilege escalation"}},"security":[{"bearer":[]}],"summary":"Grant a role to a principal in a scope (delegated, attenuated)","tags":["grants"]}},"/grants/{id}":{"delete":{"operationId":"GrantsController_revoke","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Grant revoked"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not authorized to revoke"}},"security":[{"bearer":[]}],"summary":"Revoke a grant","tags":["grants"]}},"/service-accounts":{"get":{"operationId":"ApiKeysController_listServiceAccounts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServiceAccountListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"}},"security":[{"bearer":[]}],"summary":"List all service accounts (platform admin)","tags":["service-accounts"]},"post":{"operationId":"ApiKeysController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServiceAccountDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"apikey:create required"}},"security":[{"bearer":[]}],"summary":"Create a service account (machine principal)","tags":["service-accounts"]}},"/organizations/{organizationId}/service-accounts":{"get":{"operationId":"ApiKeysController_listOrgServiceAccounts","parameters":[{"name":"organizationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServiceAccountListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"apikey:create required in the org"}},"security":[{"bearer":[]}],"summary":"List a single organization’s service accounts (org admin)","tags":["service-accounts"]}},"/service-accounts/{serviceAccountId}/api-keys":{"get":{"operationId":"ApiKeysController_listApiKeys","parameters":[{"name":"serviceAccountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"apikey:create required in scope"}},"security":[{"bearer":[]}],"summary":"List a service account’s keys — metadata only (scoped admin)","tags":["service-accounts"]},"post":{"operationId":"ApiKeysController_issue","parameters":[{"name":"serviceAccountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueApiKeyDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssuedApiKeyResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"apikey:create required"}},"security":[{"bearer":[]}],"summary":"Issue an API key for a service account (secret shown once)","tags":["service-accounts"]}},"/service-accounts/{serviceAccountId}/grants":{"get":{"operationId":"ApiKeysController_listServiceAccountGrants","parameters":[{"name":"serviceAccountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServiceAccountGrantDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"apikey:create required in scope"}},"security":[{"bearer":[]}],"summary":"List the grants held by a service account — what its keys can do (scoped admin)","tags":["service-accounts"]}},"/api-keys/{keyId}":{"delete":{"operationId":"ApiKeysController_revoke","parameters":[{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Key revoked"},"401":{"description":"Missing or invalid token"},"403":{"description":"apikey:revoke required"}},"security":[{"bearer":[]}],"summary":"Revoke an API key","tags":["service-accounts"]}},"/service-accounts/me":{"get":{"operationId":"ServiceAccountIntrospectionController_me","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountIdentityDto"}}}},"401":{"description":"Missing, malformed, invalid or revoked API key"}},"security":[{"api-key":[]}],"summary":"Introspect the calling service account (authenticated by X-API-Key)","tags":["service-accounts"]}},"/roles":{"get":{"operationId":"RolesController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleDto"}}}}}},"security":[{"bearer":[]}],"summary":"List the fixed role catalog","tags":["roles"]}},"/users/lookup":{"get":{"operationId":"UsersController_lookup","parameters":[{"name":"email","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserLookupDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not authorized to look up users"},"404":{"description":"No user with that email"}},"security":[{"bearer":[]}],"summary":"Resolve an email to a principal id (admin only)","tags":["users"]}},"/users":{"get":{"operationId":"UsersController_listAdmin","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserAdminListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing user:read permission"}},"security":[{"bearer":[]}],"summary":"Admin global list of ALL users, enriched with a roles summary and grant count (user:read — platform admin only, PII)","tags":["users"]}},"/users/{id}":{"get":{"operationId":"UsersController_detail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserAdminDetailDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing user:read permission"},"404":{"description":"User not found"}},"security":[{"bearer":[]}],"summary":"Admin detail of one user: grants by scope (with resolved names), organizations and recent activity (user:read — platform admin only, PII)","tags":["users"]}},"/notifications/mine":{"get":{"operationId":"NotificationsController_getMyNotifications","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMyNotificationsResponseDto"}}}},"401":{"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Get my in-app notifications","tags":["notifications"]}},"/notifications/{id}/read":{"post":{"operationId":"NotificationsController_markRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""},"401":{"description":"Unauthorized"},"403":{"description":"Not the owner of this notification"},"404":{"description":"Notification not found"}},"security":[{"bearer":[]}],"summary":"Mark a notification as read (owner only)","tags":["notifications"]}},"/notifications/read-all":{"post":{"operationId":"NotificationsController_markAllRead","parameters":[],"responses":{"204":{"description":""},"401":{"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Mark all my notifications as read","tags":["notifications"]}},"/emergencies/{emergencyId}/resources":{"post":{"description":"Open to any authenticated user. A trusted integration may also register on behalf of a third party with its service-account API key when it holds `resource:register` and includes the `author` block (#235).","operationId":"ResourcesController_create","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResourceDto"}}}},"responses":{"201":{"description":"Resource registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResourceResponseDto"}}}},"400":{"description":"Invalid request body or UUID, or missing author for an API key"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"Service account lacks the resource:register grant at this scope"}},"security":[{"api-key":[]},{"bearer":[]}],"summary":"Register a resource for an emergency (requires authentication)","tags":["resources"]}},"/resources/{resourceId}/inventory-entries":{"post":{"operationId":"ResourcesController_recordInventoryEntryAction","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordInventoryEntryDto"}}}},"responses":{"204":{"description":"Inventory entry recorded"},"400":{"description":"Invalid request body or UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:receive for this point"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Record a manual inventory entry into a point stock (operator/coordinator) — #9","tags":["resources"]}},"/resources/{resourceId}/inventory":{"get":{"operationId":"ResourcesController_getMyInventoryAction","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not owner nor coordinator"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Read a point declared inventory in full (owner or coordinator)","tags":["resources"]},"put":{"operationId":"ResourcesController_updateMyInventoryAction","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInventoryDto"}}}},"responses":{"204":{"description":"Inventory replaced"},"400":{"description":"Invalid request body or UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not owner nor coordinator"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Replace a point declared inventory (owner or coordinator) — #263","tags":["resources"]}},"/resources/{resourceId}/verify":{"post":{"operationId":"ResourcesController_verifyResource","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResourceDto"}}}},"responses":{"204":{"description":"Resource verified"},"400":{"description":"Invalid verification level or UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Verify a resource (coordinator of the resource's emergency only)","tags":["resources"]}},"/resources/{resourceId}/publish":{"post":{"operationId":"ResourcesController_publishResource","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Resource published"},"400":{"description":"Invalid UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Resource not found"},"409":{"description":"Resource not verified yet"}},"security":[{"bearer":[]}],"summary":"Publish a resource (verifier or coordinator of the resource's emergency)","tags":["resources"]}},"/resources/{resourceId}":{"patch":{"operationId":"ResourcesController_editResourceFields","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditResourceDto"}}}},"responses":{"204":{"description":"Resource edited"},"400":{"description":"Missing reason or resource is discarded"},"401":{"description":"Missing or invalid token"},"403":{"description":"Validator/coordinator role required"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Edit a resource during verification (validator/coordinator). Requires a reason; recorded in the audit trail.","tags":["resources"]}},"/resources/{resourceId}/discard":{"post":{"operationId":"ResourcesController_discardResourceAction","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscardResourceDto"}}}},"responses":{"204":{"description":"Resource discarded"},"400":{"description":"Missing reason or resource is not pending verification"},"401":{"description":"Missing or invalid token"},"403":{"description":"Validator/coordinator role required"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Discard a resource during verification (validator/coordinator). Requires a reason; recorded in the audit trail.","tags":["resources"]}},"/resources/{resourceId}/status":{"post":{"operationId":"ResourcesController_updateResourceStatus","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateResourcePublicStatusDto"}}}},"responses":{"204":{"description":"Status updated"},"400":{"description":"Invalid status transition or UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not authorized to update this resource status"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Update the operational public status of a resource (owner or coordinator)","tags":["resources"]}},"/resources/mine":{"get":{"description":"Union of the resources the principal owns and the ones reached through an entity-scoped grant (e.g. `point_manager`). Unlike `/emergencies/{emergencyId}/resources/mine`, this needs no emergency in the path, so it also serves principals whose only grant is entity-scoped (#285). Each row carries its emergency id and slug.","operationId":"ResourcesController_listMineAcrossEmergencies","parameters":[],"responses":{"200":{"description":"Resources the principal manages","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyManagedResourceDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List the resources the authenticated principal manages, across every emergency","tags":["resources"]}},"/emergencies/{emergencyId}/resources/mine":{"get":{"operationId":"ResourcesController_listMyResources","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"List of own resources","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ResourceViewDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List resources owned by the authenticated user in an emergency","tags":["resources"]}},"/resources/{resourceId}/validity-reports":{"post":{"operationId":"ResourcesController_submitValidityReport","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportResourceValidityDto"}}}},"responses":{"201":{"description":"Report recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportResourceValidityResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"The owner cannot report their own resource"},"404":{"description":"Resource not found"},"409":{"description":"Resource is not publicly visible"}},"security":[{"bearer":[]}],"summary":"Report a resource as closed / nonexistent / moved / outdated (any authenticated user)","tags":["resources"]},"get":{"operationId":"ResourcesController_listValidityReports","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Validity reports for the resource","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ValidityReportDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"}},"security":[{"bearer":[]}],"summary":"List the citizen validity reports of a resource (coordinator)","tags":["resources"]}},"/resources/{resourceId}/dispute/resolve":{"post":{"operationId":"ResourcesController_resolveDisputeAction","parameters":[{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResourceDisputeDto"}}}},"responses":{"204":{"description":"Dispute resolved"},"400":{"description":"Missing reason or invalid resolution"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Resource not found"},"409":{"description":"Resource is not disputed"}},"security":[{"bearer":[]}],"summary":"Resolve a disputed resource (coordinator): confirm closure, mark invalid or dismiss. Requires a reason; recorded in the audit trail.","tags":["resources"]}},"/emergencies/{emergencyId}/coordination/queue":{"get":{"operationId":"CoordinationController_list","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (max 100)","schema":{"default":50,"example":50,"type":"number"}},{"name":"type","required":false,"in":"query","description":"Filter the queue by resource type","schema":{"example":"collection_point","type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"]}},{"name":"q","required":false,"in":"query","description":"Full-text search string matched against name, address, and city (case-insensitive, max 100 chars)","schema":{"example":"cruz roja","type":"string"}}],"responses":{"200":{"description":"Paged list of resources pending verification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedResourcesDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required for this emergency"},"404":{"description":"Emergency not found"}},"security":[{"bearer":[]}],"summary":"Get the verification queue for an emergency (paginated + searchable)","tags":["resources"]}},"/emergencies/{emergencyId}/coordination/disputed":{"get":{"operationId":"CoordinationController_disputedQueue","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Disputed resources for the emergency","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DisputedResourceDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required for this emergency"}},"security":[{"bearer":[]}],"summary":"List resources flagged as disputed by citizens, with a reason breakdown","tags":["resources"]}},"/emergencies/{emergencyId}/public/resources":{"get":{"operationId":"PublicController_list","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (max 100)","schema":{"default":50,"example":50,"type":"number"}},{"name":"category","required":false,"in":"query","description":"Filter by category slug","schema":{"example":"water","type":"string"}},{"name":"country","required":false,"in":"query","description":"Filter by ISO 3166-1 alpha-2 country code","schema":{"example":"VE","type":"string"}},{"name":"q","required":false,"in":"query","description":"Full-text search string matched against name, address, and city (case-insensitive, max 100 chars)","schema":{"example":"caritas","type":"string"}}],"responses":{"200":{"description":"Paged list of published resources","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedResourcesDto"}}}}},"summary":"List published resources for an emergency (paginated + filterable)","tags":["public"]}},"/emergencies/{emergencyId}/public/resources/nearby":{"get":{"operationId":"PublicController_nearby","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"lat","required":true,"in":"query","description":"Latitude between -90 and 90","schema":{"example":10.4806,"type":"number"}},{"name":"lng","required":true,"in":"query","description":"Longitude between -180 and 180","schema":{"example":-66.9036,"type":"number"}},{"name":"radius","required":true,"in":"query","description":"Search radius in meters (max 100000)","schema":{"example":5000,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Max results (default 50, max 100)","schema":{"example":50,"type":"number"}}],"responses":{"200":{"description":"Resources within radius ordered by distance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NearbyResourcesResponseDto"}}}}},"summary":"Find visible resources near a GPS point, ordered by distance","tags":["public"]}},"/emergencies/{emergencyId}/public/resources/in-bounds":{"get":{"operationId":"PublicController_inBounds","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"minLat","required":true,"in":"query","description":"South latitude bound (-90 to 90)","schema":{"example":10.3,"type":"number"}},{"name":"minLng","required":true,"in":"query","description":"West longitude bound (-180 to 180)","schema":{"example":-67.2,"type":"number"}},{"name":"maxLat","required":true,"in":"query","description":"North latitude bound (-90 to 90)","schema":{"example":10.7,"type":"number"}},{"name":"maxLng","required":true,"in":"query","description":"East longitude bound (-180 to 180)","schema":{"example":-66.6,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Max results (default 500, max 1000)","schema":{"example":500,"type":"number"}}],"responses":{"200":{"description":"Resources within the bounding box","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InBoundsResourcesDto"}}}}},"summary":"Find visible resources within a geographic bounding box","tags":["public"]}},"/emergencies/{emergencyId}/public/resources/facets":{"get":{"operationId":"PublicController_facets","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Facets for filtering visible resources","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceFacetsDto"}}}}},"summary":"Get facets (counts by category and country) for visible resources","tags":["public"]}},"/emergencies/{emergencyId}/public/resources/{resourceId}":{"get":{"operationId":"PublicController_getOne","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"resourceId","required":true,"in":"path","description":"Resource UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"The published resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceDetailViewDto"}}}},"404":{"description":"Resource not found or not public"}},"summary":"Get a single published resource by id","tags":["public"]}},"/resources":{"get":{"operationId":"AdminResourcesController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-based)","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (max 100)","schema":{"default":50,"example":50,"type":"number"}},{"name":"emergencyId","required":false,"in":"query","description":"Restrict to one emergency. Omit for a global, cross-emergency list.","schema":{"format":"uuid","example":"11111111-1111-4111-8111-111111111111","type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by resource type","schema":{"example":"collection_point","type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"]}},{"name":"status","required":false,"in":"query","description":"Filter by operational status (includes hidden/closed — admin only)","schema":{"example":"hidden","type":"string","enum":["hidden","active","saturated","paused","closed","preparing","assisted"]}},{"name":"verification","required":false,"in":"query","description":"Filter by verification level","schema":{"example":"unverified","type":"string","enum":["unverified","verified","official","rejected"]}},{"name":"q","required":false,"in":"query","description":"Full-text search matched against name, address and city (case-insensitive, max 100 chars)","schema":{"example":"cruz roja","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminResourcesDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing resource:read at platform"}},"security":[{"bearer":[]}],"summary":"Admin global list of ALL resources — every emergency, status and verification level (resource:read at platform scope — admin only)","tags":["resources"]}},"/resources/{id}":{"get":{"operationId":"AdminResourcesController_detail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceAdminDetailDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing resource:read at platform"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"Admin detail of one resource of ANY status, with declared inventory and citizen validity reports (resource:read at platform scope — admin only)","tags":["resources"]}},"/recipient-types":{"get":{"operationId":"RecipientTypesController_list","parameters":[],"responses":{"200":{"description":"Recipient types ordered by sort","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecipientTypeDto"}}}}}},"summary":"List the recipient-type taxonomy for final recipients (extensible)","tags":["public"]}},"/templates":{"post":{"operationId":"TemplatesController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateDto"}}}},"responses":{"201":{"description":"Template created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"}},"security":[{"bearer":[]}],"summary":"Create an emergency template (admin only)","tags":["templates"]},"get":{"operationId":"TemplatesController_list","parameters":[],"responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TemplateViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"}},"security":[{"bearer":[]}],"summary":"List all emergency templates (admin only)","tags":["templates"]}},"/templates/{id}":{"delete":{"operationId":"TemplatesController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Template UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Template deleted"},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"},"404":{"description":"Template not found"}},"security":[{"bearer":[]}],"summary":"Delete an emergency template (admin only)","tags":["templates"]}},"/emergencies":{"post":{"operationId":"EmergenciesController_createEmergency","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmergencyDto"}}}},"responses":{"201":{"description":"Emergency created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmergencyResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"emergency:create required"},"409":{"description":"Slug already exists"}},"security":[{"bearer":[]}],"summary":"Create an emergency (emergency:create)","tags":["emergencies"]},"get":{"operationId":"EmergenciesController_list","parameters":[],"responses":{"200":{"description":"List of active emergencies","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EmergencyViewDto"}}}}}},"summary":"List active emergencies","tags":["emergencies"]}},"/emergencies/mine":{"get":{"operationId":"EmergenciesController_listMine","parameters":[],"responses":{"200":{"description":"Emergencies the principal holds a grant in — including paused/closed — each with the role ids held at that scope","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyEmergencyViewDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List the emergencies the authenticated principal is granted into (any status)","tags":["emergencies"]}},"/emergencies/by-slug/{slug}":{"get":{"operationId":"EmergenciesController_getBySlugRoute","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Emergency found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmergencyViewDto"}}}},"404":{"description":"Emergency not found"}},"summary":"Get emergency by slug","tags":["emergencies"]}},"/emergencies/from-template":{"post":{"operationId":"EmergenciesController_createFromTemplateRoute","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmergencyFromTemplateDto"}}}},"responses":{"201":{"description":"Emergency created from template","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmergencyResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"emergency:create required"},"404":{"description":"Template not found"},"409":{"description":"Slug already exists"}},"security":[{"bearer":[]}],"summary":"Create an emergency from a template (emergency:create)","tags":["emergencies"]}},"/emergencies/{emergencyId}/pause":{"post":{"operationId":"EmergenciesController_pauseEmergency","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Emergency paused"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Emergency not found"},"409":{"description":"Invalid transition (emergency is not active)"}},"security":[{"bearer":[]}],"summary":"Pause an active emergency (coordinator only)","tags":["emergencies"]}},"/emergencies/{emergencyId}/resume":{"post":{"operationId":"EmergenciesController_resumeEmergency","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Emergency resumed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Emergency not found"},"409":{"description":"Invalid transition (emergency is not paused)"}},"security":[{"bearer":[]}],"summary":"Resume a paused emergency (coordinator only)","tags":["emergencies"]}},"/emergencies/{emergencyId}/announcement":{"put":{"operationId":"EmergenciesController_publishEmergencyAnnouncement","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishAnnouncementDto"}}}},"responses":{"204":{"description":"Announcement published"},"400":{"description":"Invalid body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Emergency not found"}},"security":[{"bearer":[]}],"summary":"Publish official announcement for an emergency (coordinator only)","tags":["emergencies"]}},"/emergencies/{emergencyId}/resource-dispute-threshold":{"put":{"operationId":"EmergenciesController_setResourceDisputeThreshold","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetDisputeThresholdDto"}}}},"responses":{"204":{"description":"Umbral actualizado"},"400":{"description":"Invalid body"},"401":{"description":"Missing or invalid token"},"403":{"description":"emergency:configure permission required"},"404":{"description":"Emergency not found"}},"security":[{"bearer":[]}],"summary":"Fijar (o limpiar) el umbral de disputa por reportes para una emergencia","tags":["emergencies"]}},"/emergencies/{emergencyId}/needs":{"post":{"description":"Open to any authenticated user (a citizen submits; a coordinator validates later). A trusted integration may also create on behalf of a third party with its service-account API key when it holds `need:create` and includes the `author` block (#235).","operationId":"NeedsController_create","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNeedDto"}}}},"responses":{"201":{"description":"Need created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNeedResponseDto"}}}},"400":{"description":"Invalid request body or UUID, or missing author for an API key"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"Service account lacks the need:create grant at this scope"}},"security":[{"api-key":[]},{"bearer":[]}],"summary":"Create a need for an emergency (authenticated requester)","tags":["needs"]}},"/emergencies/{emergencyId}/public/needs":{"get":{"operationId":"NeedsController_listPublic","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"category","required":false,"in":"query","description":"Filter by item category slug (needs with at least one item of this category). Core slugs: food, water, hygiene, clothing, medical, shelter, tools, other, medicines, medical_equipment, medical_supplies, medical_personnel, food_fresh, food_non_perishable, hygiene_infantile, hygiene_personal, tools_extraction, other_pets.","schema":{"example":"water","type":"string"}},{"name":"priority","required":false,"in":"query","description":"Filter by need priority","schema":{"enum":["low","medium","high","urgent"],"type":"string"}},{"name":"resourceId","required":false,"in":"query","description":"Filter to needs linked to this resource / final recipient","schema":{"format":"uuid","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size for pagination (1-100). Omit to return all.","schema":{"type":"string"}},{"name":"offset","required":false,"in":"query","description":"Number of items to skip (pagination). Defaults to 0.","schema":{"type":"string"}}],"responses":{"200":{"description":"List of validated needs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NeedViewDto"}}}}}},"summary":"List validated needs for an emergency (public)","tags":["needs"]}},"/emergencies/{emergencyId}/public/needs/nearby":{"get":{"operationId":"NeedsController_listNearby","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"lat","required":true,"in":"query","description":"Latitude between -90 and 90","schema":{"example":10.4806,"type":"number"}},{"name":"lng","required":true,"in":"query","description":"Longitude between -180 and 180","schema":{"example":-66.9036,"type":"number"}},{"name":"radius","required":true,"in":"query","description":"Search radius in meters (max 100000)","schema":{"example":5000,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Max results (default 50, max 100)","schema":{"example":50,"type":"number"}}],"responses":{"200":{"description":"Validated needs within radius ordered by distance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NearbyNeedsResponseDto"}}}}},"summary":"List validated needs near a GPS point, ordered by distance (public, #57)","tags":["needs"]}},"/emergencies/{emergencyId}/public/needs/in-bounds":{"get":{"operationId":"NeedsController_needsInBounds","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"minLat","required":true,"in":"query","description":"South latitude bound (-90 to 90)","schema":{"example":10.3,"type":"number"}},{"name":"minLng","required":true,"in":"query","description":"West longitude bound (-180 to 180)","schema":{"example":-67.2,"type":"number"}},{"name":"maxLat","required":true,"in":"query","description":"North latitude bound (-90 to 90)","schema":{"example":10.7,"type":"number"}},{"name":"maxLng","required":true,"in":"query","description":"East longitude bound (-180 to 180)","schema":{"example":-66.6,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Max results (default 500, max 1000)","schema":{"example":500,"type":"number"}}],"responses":{"200":{"description":"Validated needs within the bounding box","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InBoundsNeedsDto"}}}}},"summary":"List validated needs within a geographic bounding box (public)","tags":["needs"]}},"/emergencies/{emergencyId}/needs/queue":{"get":{"operationId":"NeedsController_listQueue","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"category","required":false,"in":"query","description":"Filter by item category slug (needs with at least one item of this category). Core slugs: food, water, hygiene, clothing, medical, shelter, tools, other, medicines, medical_equipment, medical_supplies, medical_personnel, food_fresh, food_non_perishable, hygiene_infantile, hygiene_personal, tools_extraction, other_pets.","schema":{"example":"water","type":"string"}},{"name":"priority","required":false,"in":"query","description":"Filter by need priority","schema":{"enum":["low","medium","high","urgent"],"type":"string"}}],"responses":{"200":{"description":"List of pending needs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NeedViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required for this emergency"}},"security":[{"bearer":[]}],"summary":"Get pending needs queue for an emergency (coordinator only)","tags":["needs"]}},"/needs/{needId}/validate":{"post":{"operationId":"NeedsController_validate","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Need validated"},"400":{"description":"Need is not in pending status"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Validate a need (coordinator of the need's emergency only)","tags":["needs"]}},"/needs/{needId}":{"patch":{"operationId":"NeedsController_edit","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditNeedDto"}}}},"responses":{"204":{"description":"Need edited"},"400":{"description":"Missing reason or need is in a terminal status"},"401":{"description":"Missing or invalid token"},"403":{"description":"Validator/coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Edit a need during validation (validator/coordinator). Requires a reason; recorded in the audit trail.","tags":["needs"]}},"/needs/{needId}/discard":{"post":{"operationId":"NeedsController_discard","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscardNeedDto"}}}},"responses":{"204":{"description":"Need discarded (rejected)"},"400":{"description":"Missing reason or need is not in pending status"},"401":{"description":"Missing or invalid token"},"403":{"description":"Validator/coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Discard a need during validation (validator/coordinator). Requires a reason; recorded in the audit trail.","tags":["needs"]}},"/needs/{needId}/assign-manager":{"post":{"operationId":"NeedsController_assignManager","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignNeedManagerDto"}}}},"responses":{"204":{"description":"Manager assigned"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Assign a managing organization to a need (coordinator of the need's emergency only)","tags":["needs"]}},"/needs/{needId}/renew":{"post":{"operationId":"NeedsController_renew","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Need renewed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NeedViewDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Renew a need (reset expiresAt to now+48h) — coordinator only","tags":["needs"]}},"/emergencies/{emergencyId}/needs/expired":{"get":{"operationId":"NeedsController_listExpired","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"List of expired validated needs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NeedViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"}},"security":[{"bearer":[]}],"summary":"List expired needs for an emergency (coordinator only)","tags":["needs"]}},"/needs/{needId}/volunteer-suggestions":{"get":{"operationId":"NeedsController_getVolunteerSuggestions","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of suggestions (default 20)","schema":{"type":"number"}}],"responses":{"200":{"description":"List of suggested volunteers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VolunteerSuggestionDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Suggest available volunteers for a personnel need (coordinator only)","tags":["needs"]}},"/needs/{needId}/create-task":{"post":{"description":"Creates one Task linked to the need and optionally assigns volunteers. Reuses existing CreateTask / AssignVolunteerToTask logic.","operationId":"NeedsController_createTask","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskFromNeedDto"}}}},"responses":{"201":{"description":"Task created and volunteers assigned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTaskFromNeedDto"}}}},"400":{"description":"Invalid body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Create a task from a personnel need (coordinator only)","tags":["needs"]}},"/organizations":{"post":{"operationId":"OrganizationsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationDto"}}}},"responses":{"201":{"description":"Organization created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationResponseDto"}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"Create a new organization (authenticated users)","tags":["organizations"]},"get":{"operationId":"OrganizationsController_list","parameters":[],"responses":{"200":{"description":"All organizations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationViewDto"}}}}}},"summary":"List all organizations (public)","tags":["organizations"]}},"/organizations/mine":{"get":{"operationId":"OrganizationsController_mine","parameters":[],"responses":{"200":{"description":"User organizations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationViewDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List organizations the authenticated user belongs to","tags":["organizations"]}},"/organizations/admin":{"get":{"operationId":"OrganizationsController_listAdmin","parameters":[],"responses":{"200":{"description":"All organizations (admin view)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationAdminListItemDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing org:read permission"}},"security":[{"bearer":[]}],"summary":"Admin global list of ALL organizations, enriched with member count and accreditation status (org:read)","tags":["organizations"]}},"/organizations/{id}":{"get":{"operationId":"OrganizationsController_detail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Organization detail (admin view)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationAdminDetailDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing org:read permission"},"404":{"description":"Organization not found"}},"security":[{"bearer":[]}],"summary":"Admin detail of one organization: members, service accounts/API keys, accreditations and emergencies (org:read)","tags":["organizations"]}},"/organizations/{id}/members":{"get":{"operationId":"OrganizationsController_listMembers","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Organization members","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationMemberDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a member of this organization"}},"security":[{"bearer":[]}],"summary":"List members of an organization (must be a member)","tags":["organizations"]},"post":{"operationId":"OrganizationsController_addMember","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberDto"}}}},"responses":{"201":{"description":"Member added"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the owner can add members"},"404":{"description":"User not found by email"},"409":{"description":"User is already a member"}},"security":[{"bearer":[]}],"summary":"Add a member to an organization (owner only)","tags":["organizations"]}},"/organizations/{id}/members/{userId}":{"delete":{"operationId":"OrganizationsController_removeMember","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Member removed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the owner can remove members"},"404":{"description":"Member not found"}},"security":[{"bearer":[]}],"summary":"Remove a member from an organization (owner only)","tags":["organizations"]}},"/accreditations":{"post":{"operationId":"AccreditationsController_grantAccreditation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantAccreditationDto"}}}},"responses":{"201":{"description":"Accreditation granted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccreditationResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"}},"security":[{"bearer":[]}],"summary":"Grant accreditation to an organization (accreditation:grant)","tags":["accreditations"]},"get":{"operationId":"AccreditationsController_listAccreditations","parameters":[{"name":"organizationId","required":false,"in":"query","description":"Filter by organization UUID","schema":{"type":"string"}},{"name":"emergencyId","required":false,"in":"query","description":"Filter by emergency UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"List of accreditations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccreditationViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"}},"security":[{"bearer":[]}],"summary":"List accreditations (accreditation:grant)","tags":["accreditations"]}},"/accreditations/{id}":{"delete":{"operationId":"AccreditationsController_revokeAccreditation","parameters":[{"name":"id","required":true,"in":"path","description":"Accreditation UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Accreditation revoked"},"401":{"description":"Missing or invalid token"},"403":{"description":"Admin access required"},"404":{"description":"Accreditation not found"}},"security":[{"bearer":[]}],"summary":"Revoke an accreditation (accreditation:revoke)","tags":["accreditations"]}},"/geocode":{"get":{"operationId":"GeocodingController_search","parameters":[{"name":"q","required":true,"in":"query","description":"Address or place name to search","schema":{"example":"Madrid"}}],"responses":{"200":{"description":"Geocoding results (empty array when query is shorter than 3 characters)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GeocodeResultDto"}}}}},"429":{"description":"Rate limit exceeded"}},"summary":"Geocode a free-text address query (Nominatim / OpenStreetMap)","tags":["geocoding"]}},"/emergencies/{emergencyId}/metrics":{"get":{"operationId":"MetricsController_metrics","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Emergency metrics summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmergencyMetricsDto"}}}}},"summary":"Get aggregated metrics for an emergency (public)","tags":["metrics"]}},"/emergencies/{emergencyId}/offers":{"post":{"description":"Open to any authenticated user. A trusted integration may also submit on behalf of a third party with its service-account API key when it holds `offer:create` and includes the `author` block (#235).","operationId":"OffersController_submit","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitOfferDto"}}}},"responses":{"201":{"description":"Offer created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitOfferResponseDto"}}}},"400":{"description":"Invalid request body or UUID, or missing author for an API key"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"Service account lacks the offer:create grant at this scope"},"409":{"description":"Emergency is not accepting intake (paused/closed)"}},"security":[{"api-key":[]},{"bearer":[]}],"summary":"Submit a donation offer for an emergency (authenticated donor)","tags":["offers"]}},"/emergencies/{emergencyId}/offers/queue":{"get":{"operationId":"OffersController_listQueue","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"List of open offers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OfferViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"}},"security":[{"bearer":[]}],"summary":"Get open offers queue for an emergency (coordinator only)","tags":["offers"]}},"/emergencies/{emergencyId}/offers/mine":{"get":{"operationId":"OffersController_listMine","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"My offers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OfferViewDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List my offers for an emergency (authenticated donor)","tags":["offers"]}},"/needs/{needId}/offers":{"get":{"operationId":"OffersController_listForNeed","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Offers matched to the need","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OfferViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"List matched offers for a need (coordinator only)","tags":["offers"]}},"/needs/{needId}/offer-suggestions":{"get":{"operationId":"OffersController_suggestForNeed","parameters":[{"name":"needId","required":true,"in":"path","description":"Need UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Suggested offers sorted by proximity","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OfferViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Need not found"}},"security":[{"bearer":[]}],"summary":"Suggest open offers for a need (coordinator only), sorted by proximity","tags":["offers"]}},"/offers/{offerId}/match":{"post":{"operationId":"OffersController_match","parameters":[{"name":"offerId","required":true,"in":"path","description":"Offer UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchOfferDto"}}}},"responses":{"204":{"description":"Offer matched to need"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Offer or need not found"},"409":{"description":"Offer is not in Open status"}},"security":[{"bearer":[]}],"summary":"Match an offer to a need (coordinator of the offer's emergency only)","tags":["offers"]}},"/offers/{offerId}":{"patch":{"operationId":"OffersController_edit","parameters":[{"name":"offerId","required":true,"in":"path","description":"Offer UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditOfferDto"}}}},"responses":{"204":{"description":"Offer edited"},"400":{"description":"Missing reason or offer is in a terminal status"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Offer not found"}},"security":[{"bearer":[]}],"summary":"Edit a donation offer (coordinator). Requires a reason; recorded in the audit trail.","tags":["offers"]}},"/offers/{offerId}/discard":{"post":{"operationId":"OffersController_discard","parameters":[{"name":"offerId","required":true,"in":"path","description":"Offer UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscardOfferDto"}}}},"responses":{"204":{"description":"Offer discarded (cancelled)"},"400":{"description":"Missing reason or offer cannot be discarded in its status"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Offer not found"}},"security":[{"bearer":[]}],"summary":"Discard a donation offer (coordinator). Requires a reason; recorded in the audit trail.","tags":["offers"]}},"/offers/{offerId}/fulfill":{"post":{"operationId":"OffersController_fulfill","parameters":[{"name":"offerId","required":true,"in":"path","description":"Offer UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Offer fulfilled"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Offer not found"},"409":{"description":"Offer is not in Matched status"}},"security":[{"bearer":[]}],"summary":"Mark an offer as fulfilled (coordinator of the offer's emergency only)","tags":["offers"]}},"/offers/{offerId}/cancel":{"post":{"operationId":"OffersController_cancel","parameters":[{"name":"offerId","required":true,"in":"path","description":"Offer UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Offer cancelled"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the offer owner or a coordinator can cancel"},"404":{"description":"Offer not found"},"409":{"description":"Offer cannot be cancelled in its current status"}},"security":[{"bearer":[]}],"summary":"Cancel an offer (owner or coordinator)","tags":["offers"]}},"/emergencies/{emergencyId}/donation-intakes":{"post":{"operationId":"DonationIntakesController_create","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDonationIntakeDto"}}}},"responses":{"201":{"description":"Intake created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDonationIntakeResponseDto"}}}},"400":{"description":"Invalid request body"},"409":{"description":"Emergency is not accepting intake (paused/closed)"},"422":{"description":"Invalid collection point"},"429":{"description":"Rate limit exceeded"}},"summary":"Pre-register a donation at a collection point (public)","tags":["donation-intakes"]}},"/emergencies/{emergencyId}/donation-intakes/lookup-contact":{"post":{"operationId":"DonationIntakesController_lookupContact","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupDonorByContactDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupDonorByContactResponseDto"}}}},"400":{"description":"Invalid request body"},"429":{"description":"Rate limit exceeded"}},"summary":"Recognize a returning donor by phone or email (public)","tags":["donation-intakes"]}},"/emergencies/{emergencyId}/donation-intakes/by-code/{code}":{"get":{"operationId":"DonationIntakesController_trackByCode","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}},{"name":"code","required":true,"in":"path","schema":{"example":"ACO-7F3K","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DonationIntakeTrackingDto"}}}},"404":{"description":"No donation with that code"},"429":{"description":"Rate limit exceeded"}},"summary":"Track a donation by its code (public, no PII)","tags":["donation-intakes"]}},"/resources/{resourceId}/donation-intakes/incoming-summary":{"get":{"operationId":"DonationIntakesController_incomingSummary","parameters":[{"name":"resourceId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IncomingSummaryDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:read"}},"security":[{"bearer":[]}],"summary":"Aggregated forecast of incoming material for a collection point","tags":["donation-intakes"]}},"/me/donation-intakes":{"get":{"operationId":"DonationIntakesController_myDonations","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyDonationIntakeDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List the authenticated donor's own donations","tags":["donation-intakes"]}},"/donation-intakes/{intakeId}":{"patch":{"operationId":"DonationIntakesController_update","parameters":[{"name":"intakeId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDonationIntakeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicDonationIntakeDto"}}}},"400":{"description":"Invalid request body"},"403":{"description":"Contact or code mismatch"},"404":{"description":"Intake not found"},"409":{"description":"Intake already processed"},"429":{"description":"Rate limit exceeded"}},"summary":"Update a pending intake (public, requires code + contact)","tags":["donation-intakes"]},"get":{"operationId":"DonationIntakesController_getById","parameters":[{"name":"intakeId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DonationIntakeViewDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:read"},"404":{"description":"Intake not found"}},"security":[{"bearer":[]}],"summary":"Get full intake detail with lines","tags":["donation-intakes"]}},"/emergencies/{emergencyId}/donation-intakes/search":{"get":{"operationId":"DonationIntakesController_search","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}},{"name":"q","required":true,"in":"query","description":"Phone, email, code or name","schema":{"example":"ACO-7F3K","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DonationIntakeSearchHitDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:read"}},"security":[{"bearer":[]}],"summary":"Search intakes by phone, email, code or name","tags":["donation-intakes"]}},"/resources/{resourceId}/intake-link":{"get":{"operationId":"DonationIntakesController_getIntakeLink","parameters":[{"name":"resourceId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntakeDeepLinkDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:read"},"404":{"description":"Resource not found"},"422":{"description":"Resource is not a published collection point"}},"security":[{"bearer":[]}],"summary":"Get the public deep link for pre-registering at this acopio","tags":["donation-intakes"]}},"/resources/{resourceId}/intake-qr":{"get":{"operationId":"DonationIntakesController_getIntakeQr","parameters":[{"name":"resourceId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"PNG image","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:read"},"404":{"description":"Resource not found"},"422":{"description":"Resource is not a published collection point"}},"security":[{"bearer":[]}],"summary":"Generate a QR code PNG for the intake deep link of this acopio","tags":["donation-intakes"]}},"/resources/{resourceId}/donation-intakes/pending":{"get":{"operationId":"DonationIntakesController_listPending","parameters":[{"name":"resourceId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DonationIntakeSearchHitDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:read"},"404":{"description":"Resource not found"}},"security":[{"bearer":[]}],"summary":"List pending intakes for a collection point","tags":["donation-intakes"]}},"/donation-intakes/{intakeId}/receive":{"post":{"operationId":"DonationIntakesController_receive","parameters":[{"name":"intakeId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveDonationIntakeDto"}}}},"responses":{"204":{"description":"Intake marked as received"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:receive"},"404":{"description":"Intake not found"},"409":{"description":"Intake already processed"}},"security":[{"bearer":[]}],"summary":"Confirm physical reception of a pending intake","tags":["donation-intakes"]}},"/donation-intakes/{intakeId}/reject":{"post":{"operationId":"DonationIntakesController_reject","parameters":[{"name":"intakeId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectDonationIntakeDto"}}}},"responses":{"204":{"description":"Intake marked as rejected"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:receive"},"404":{"description":"Intake not found"},"409":{"description":"Intake already processed"}},"security":[{"bearer":[]}],"summary":"Reject a pending intake at reception","tags":["donation-intakes"]}},"/donation-intakes/{intakeId}/incomplete":{"post":{"operationId":"DonationIntakesController_markIncomplete","parameters":[{"name":"intakeId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkDonationIntakeIncompleteDto"}}}},"responses":{"204":{"description":"Intake marked as incomplete"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing intake:receive"},"404":{"description":"Intake not found"},"409":{"description":"Intake already processed"}},"security":[{"bearer":[]}],"summary":"Mark a pending intake as incomplete (material mismatch)","tags":["donation-intakes"]}},"/logistics/capacities":{"post":{"operationId":"LogisticsController_publish","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishCapacityDto"}}}},"responses":{"201":{"description":"Capacity published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishCapacityResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing capacity:publish permission"},"409":{"description":"Emergency is not accepting intake (paused/closed)"}},"security":[{"bearer":[]}],"summary":"Publish a transport-capacity offer (authenticated, citizen-grade)","tags":["logistics"]}},"/logistics/capacities/{id}/withdraw":{"post":{"operationId":"LogisticsController_withdraw","parameters":[{"name":"id","required":true,"in":"path","description":"Capacity UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Capacity withdrawn"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the provider or a coordinator can withdraw"},"404":{"description":"Capacity not found"},"409":{"description":"Capacity cannot be withdrawn in its current status"}},"security":[{"bearer":[]}],"summary":"Withdraw a transport-capacity offer (provider or coordinator)","tags":["logistics"]}},"/emergencies/{emergencyId}/logistics/capacities":{"get":{"operationId":"LogisticsController_list","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"mode","required":false,"in":"query","description":"Filter by mode","schema":{"type":"string","enum":["road","sea","air"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["available","reserved","withdrawn"]}},{"name":"availableFrom","required":false,"in":"query","description":"Keep capacities available at/after this instant (ISO-8601)","schema":{"example":"2026-07-05T00:00:00.000Z","type":"string"}},{"name":"availableTo","required":false,"in":"query","description":"Keep capacities available at/before this instant (ISO-8601)","schema":{"example":"2026-07-10T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Capacities","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CapacityViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing capacity:read permission"}},"security":[{"bearer":[]}],"summary":"List transport capacities for an emergency (coordinator/verifier)","tags":["logistics"]}},"/logistics/shipments":{"post":{"operationId":"ShipmentController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShipmentDto"}}}},"responses":{"201":{"description":"Shipment created (planned)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShipmentResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"},"409":{"description":"Emergency is not accepting intake (paused/closed)"}},"security":[{"bearer":[]}],"summary":"Create a shipment / expedición (coordinator)","tags":["logistics"]}},"/logistics/shipments/{id}/assign-capacity":{"post":{"operationId":"ShipmentController_assignCapacity","parameters":[{"name":"id","required":true,"in":"path","description":"Shipment UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignCapacityToShipmentDto"}}}},"responses":{"204":{"description":"Capacity assigned"},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the shipment emergency"},"404":{"description":"Shipment not found"},"409":{"description":"Shipment is not in planned status"}},"security":[{"bearer":[]}],"summary":"Assign a transport capacity (and optional carrier) (coordinator)","tags":["logistics"]}},"/logistics/shipments/{id}/in-transit":{"post":{"operationId":"ShipmentController_markInTransit","parameters":[{"name":"id","required":true,"in":"path","description":"Shipment UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Shipment marked in transit"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the assigned carrier or a coordinator can act"},"404":{"description":"Shipment not found"},"409":{"description":"Shipment is not in assigned status"}},"security":[{"bearer":[]}],"summary":"Mark a shipment in transit (assigned carrier or coordinator)","tags":["logistics"]}},"/logistics/shipments/{id}/deliver":{"post":{"operationId":"ShipmentController_deliver","parameters":[{"name":"id","required":true,"in":"path","description":"Shipment UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Shipment delivered"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the assigned carrier or a coordinator can act"},"404":{"description":"Shipment not found"},"409":{"description":"Shipment is not in transit"}},"security":[{"bearer":[]}],"summary":"Confirm a shipment delivery (assigned carrier or coordinator)","tags":["logistics"]}},"/logistics/shipments/{id}/cancel":{"post":{"operationId":"ShipmentController_cancel","parameters":[{"name":"id","required":true,"in":"path","description":"Shipment UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Shipment cancelled"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the shipment emergency"},"404":{"description":"Shipment not found"},"409":{"description":"Shipment cannot be cancelled in its current status"}},"security":[{"bearer":[]}],"summary":"Cancel a shipment (coordinator)","tags":["logistics"]}},"/logistics/shipments/mine":{"get":{"operationId":"ShipmentController_listMine","parameters":[{"name":"emergencyId","required":false,"in":"query","description":"Optional emergency scope; omit to list across emergencies","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"My shipments","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ShipmentViewDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List my shipments as a carrier (\"mis expediciones\")","tags":["logistics"]}},"/emergencies/{emergencyId}/logistics/shipments":{"get":{"operationId":"ShipmentController_list","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["planned","assigned","in_transit","delivered","failed","cancelled"]}}],"responses":{"200":{"description":"Shipments","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ShipmentViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing shipment:read permission"}},"security":[{"bearer":[]}],"summary":"List shipments for an emergency (coordinator/verifier)","tags":["logistics"]}},"/logistics/shipments/{id}/capacity-suggestions":{"get":{"operationId":"ShipmentController_capacitySuggestions","parameters":[{"name":"id","required":true,"in":"path","description":"Shipment UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Compatible capacities ranked by proximity/coverage fit","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CapacityViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the shipment emergency"},"404":{"description":"Shipment not found"}},"security":[{"bearer":[]}],"summary":"Suggest compatible transport capacities for a shipment, ranked (coordinator)","tags":["logistics"]}},"/categories":{"get":{"operationId":"CategoriesController_list","parameters":[{"name":"locale","required":false,"in":"query","description":"Preferred locale","schema":{"type":"string"}},{"name":"Accept-Language","in":"header","description":"Fallback locale header (es, en or a custom translation locale)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The category taxonomy","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CategoryDto"}}}}}},"summary":"List the shared category taxonomy (slug + labels + hierarchy)","tags":["categories"]}},"/admin/categories":{"get":{"operationId":"CategoriesAdminController_list","parameters":[{"name":"locale","required":false,"in":"query","description":"Preferred locale","schema":{"type":"string"}},{"name":"accept-language","required":true,"in":"header","schema":{"type":"string"}},{"name":"Accept-Language","in":"header","description":"Fallback locale header (es, en or a custom translation locale)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CategoryAdminDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"}},"security":[{"bearer":[]}],"summary":"List the full category taxonomy for admin (archived included)","tags":["categories-admin"]},"post":{"operationId":"CategoriesAdminController_create","parameters":[{"name":"locale","required":true,"in":"query","schema":{"type":"string"}},{"name":"accept-language","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryAdminDto"}}}},"400":{"description":"Invalid category payload"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"},"409":{"description":"Category slug already exists"}},"security":[{"bearer":[]}],"summary":"Create a category or subcategory","tags":["categories-admin"]}},"/admin/categories/{slug}":{"patch":{"operationId":"CategoriesAdminController_update","parameters":[{"name":"slug","required":true,"in":"path","description":"Current category slug","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}},{"name":"accept-language","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryAdminDto"}}}},"400":{"description":"Invalid category payload"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"},"404":{"description":"Category not found"},"409":{"description":"Category slug already exists"}},"security":[{"bearer":[]}],"summary":"Update a category, subcategory or archive flag","tags":["categories-admin"]},"delete":{"operationId":"CategoriesAdminController_delete","parameters":[{"name":"slug","required":true,"in":"path","description":"Category slug to archive","schema":{"type":"string"}}],"responses":{"204":{"description":"Category archived"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"},"404":{"description":"Category not found"},"409":{"description":"Core category slug is protected and cannot be archived"}},"security":[{"bearer":[]}],"summary":"Archive a custom category (core slugs are protected)","tags":["categories-admin"]}},"/admin/attribute-definitions":{"get":{"operationId":"AttributeDefinitionsAdminController_list","parameters":[{"name":"categorySlug","required":false,"in":"query","description":"Filter by the exact category slug","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AttributeDefinitionDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"}},"security":[{"bearer":[]}],"summary":"List global attribute definitions, optionally by category","tags":["attribute-definitions-admin"]},"post":{"operationId":"AttributeDefinitionsAdminController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAttributeDefinitionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttributeDefinitionDto"}}}},"400":{"description":"Invalid attribute definition payload"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"},"404":{"description":"Category not found"}},"security":[{"bearer":[]}],"summary":"Create a global attribute definition for a family","tags":["attribute-definitions-admin"]}},"/admin/attribute-definitions/{categorySlug}/{key}":{"delete":{"operationId":"AttributeDefinitionsAdminController_archive","parameters":[{"name":"categorySlug","required":true,"in":"path","description":"Category slug of the family","schema":{"type":"string"}},{"name":"key","required":true,"in":"path","description":"Attribute key","schema":{"type":"string"}}],"responses":{"204":{"description":"Attribute definition archived"},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing catalogue:manage permission"},"404":{"description":"Attribute definition not found"}},"security":[{"bearer":[]}],"summary":"Archive (soft-delete) a global attribute definition","tags":["attribute-definitions-admin"]}},"/supplies":{"get":{"operationId":"SuppliesController_list","parameters":[{"name":"q","required":false,"in":"query","description":"Autocomplete term","schema":{"example":"advil","type":"string"}},{"name":"categorySlug","required":false,"in":"query","description":"Filter by category slug","schema":{"example":"medical_supplies","type":"string"}},{"name":"locale","required":false,"in":"query","description":"Preferred locale","schema":{"example":"en","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max items to return","schema":{"minimum":1,"maximum":50,"example":20,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"How many items to skip","schema":{"minimum":0,"example":0,"type":"number"}},{"name":"Accept-Language","in":"header","description":"Fallback locale header (es or en)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Matched supplies","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupplyDto"}}}}}},"summary":"Search the shared supply catalog (canonical names, aliases and codes)","tags":["supplies"]}},"/supplies/{id}":{"get":{"operationId":"SuppliesController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":false,"in":"query","description":"Preferred locale","schema":{}},{"name":"Accept-Language","in":"header","description":"Fallback locale header (es or en)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Supply detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplyDto"}}}}},"summary":"Get a supply by id","tags":["supplies"]}},"/admin/supplies":{"post":{"operationId":"SuppliesAdminController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupplyDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupplyResponseDto"}}}},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Crear un insumo (asigna código INS-NNNN)","tags":["supplies-admin"]},"get":{"operationId":"SuppliesAdminController_list","parameters":[{"name":"q","required":false,"in":"query","description":"Búsqueda por código o nombre","schema":{"type":"string"}},{"name":"categorySlug","required":false,"in":"query","description":"Filtra por categoría","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["active","archived"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupplyDto"}}}}},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Listar el catálogo (incluye archivados)","tags":["supplies-admin"]}},"/admin/supplies/backfill":{"get":{"operationId":"SuppliesAdminController_backfillReport","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplyLinkReportDto"}}}},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Informe de líneas legacy sin enlazar al catálogo (no-casadas)","tags":["supplies-admin"]},"post":{"operationId":"SuppliesAdminController_backfill","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplyLinkBackfillResultDto"}}}},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Backfill best-effort del supplyId de las líneas legacy (idempotente)","tags":["supplies-admin"]}},"/admin/supplies/{id}":{"get":{"operationId":"SuppliesAdminController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSupplyDto"}}}},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Detalle de gestión de un insumo","tags":["supplies-admin"]},"patch":{"operationId":"SuppliesAdminController_edit","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditSupplyDto"}}}},"responses":{"204":{"description":""},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Editar un insumo (code no editable)","tags":["supplies-admin"]}},"/admin/supplies/{id}/archive":{"post":{"operationId":"SuppliesAdminController_archive","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Archivar un insumo","tags":["supplies-admin"]}},"/admin/supplies/{id}/restore":{"post":{"operationId":"SuppliesAdminController_restore","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Reactivar un insumo archivado","tags":["supplies-admin"]}},"/admin/supplies/{id}/aliases":{"post":{"operationId":"SuppliesAdminController_addAlias","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSupplyAliasDto"}}}},"responses":{"204":{"description":""},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Añadir un alias/sinónimo a un insumo","tags":["supplies-admin"]}},"/admin/supplies/{id}/aliases/{aliasNorm}":{"delete":{"operationId":"SuppliesAdminController_removeAlias","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"aliasNorm","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Eliminar un alias del catálogo","tags":["supplies-admin"]}},"/admin/supplies/merge":{"post":{"operationId":"SuppliesAdminController_merge","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeSuppliesDto"}}}},"responses":{"204":{"description":""},"403":{"description":"Falta el permiso catalogue:manage"}},"security":[{"bearer":[]}],"summary":"Fusionar un insumo duplicado en el canónico","tags":["supplies-admin"]}},"/supplies/containers":{"post":{"operationId":"ContainerController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContainerDto"}}}},"responses":{"201":{"description":"Container created (open), with a generated code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContainerResponseDto"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"}},"security":[{"bearer":[]}],"summary":"Create a trackable container (palet/caja/lote) (coordinator)","tags":["supplies"]}},"/supplies/containers/{id}/lines":{"post":{"operationId":"ContainerController_addLine","parameters":[{"name":"id","required":true,"in":"path","description":"Container UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplyLineDto"}}}},"responses":{"204":{"description":"Line added"},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"},"404":{"description":"Container not found"},"409":{"description":"Container is sealed (lines immutable)"}},"security":[{"bearer":[]}],"summary":"Add a supply line to a container (coordinator)","tags":["supplies"]}},"/supplies/containers/{id}/lines/{index}":{"delete":{"operationId":"ContainerController_removeLine","parameters":[{"name":"id","required":true,"in":"path","description":"Container UUID","schema":{"format":"uuid","type":"string"}},{"name":"index","required":true,"in":"path","description":"Zero-based line index","schema":{"example":0,"type":"number"}}],"responses":{"204":{"description":"Line removed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"},"404":{"description":"Container not found"},"409":{"description":"Container is sealed (lines immutable)"}},"security":[{"bearer":[]}],"summary":"Remove the supply line at an index from a container (coordinator)","tags":["supplies"]}},"/supplies/containers/{id}/nest":{"post":{"operationId":"ContainerController_nest","parameters":[{"name":"id","required":true,"in":"path","description":"Container UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NestContainerDto"}}}},"responses":{"204":{"description":"Container re-parented"},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"},"404":{"description":"Container or parent not found"},"422":{"description":"Would create a cycle, or parent is a different emergency"}},"security":[{"bearer":[]}],"summary":"Nest a container under a parent, or un-nest it (coordinator)","tags":["supplies"]}},"/supplies/containers/{id}/seal":{"post":{"operationId":"ContainerController_seal","parameters":[{"name":"id","required":true,"in":"path","description":"Container UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Container sealed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"},"404":{"description":"Container not found"},"409":{"description":"Container is already sealed"}},"security":[{"bearer":[]}],"summary":"Seal (precintar) a container (coordinator)","tags":["supplies"]}},"/supplies/containers/{id}/move":{"post":{"operationId":"ContainerController_move","parameters":[{"name":"id","required":true,"in":"path","description":"Container UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveContainerDto"}}}},"responses":{"204":{"description":"Container moved"},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator of the emergency"},"404":{"description":"Container not found"}},"security":[{"bearer":[]}],"summary":"Move a container to a holder (resource ↔ shipment) (coordinator)","tags":["supplies"]}},"/supplies/containers/{id}":{"get":{"operationId":"ContainerController_get","parameters":[{"name":"id","required":true,"in":"path","description":"Container UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Container tree","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContainerTreeViewDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Not a coordinator/verifier of the emergency"},"404":{"description":"Container not found"}},"security":[{"bearer":[]}],"summary":"Get a container tree with aggregated weight/volume (coordinator/verifier)","tags":["supplies"]}},"/emergencies/{emergencyId}/supplies/containers":{"get":{"operationId":"ContainerController_list","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["pallet","box","lote"]}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["open","sealed"]}},{"name":"holderType","required":false,"in":"query","schema":{"type":"string","enum":["resource","shipment","vehicle"]}},{"name":"holderId","required":false,"in":"query","schema":{"format":"uuid","type":"string"}},{"name":"topLevelOnly","required":false,"in":"query","description":"Only top-level containers (the roots of the trees)","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Containers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContainerViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Missing container:read permission"}},"security":[{"bearer":[]}],"summary":"List containers for an emergency (coordinator/verifier)","tags":["supplies"]}},"/emergencies/{emergencyId}/volunteers":{"post":{"operationId":"VolunteersController_registerVolunteer","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterVolunteerDto"}}}},"responses":{"201":{"description":"Volunteer registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterVolunteerResponseDto"}}}},"400":{"description":"Invalid request body or UUID"},"401":{"description":"Missing or invalid token"},"409":{"description":"Emergency not accepting volunteers (paused/closed)"}},"security":[{"bearer":[]}],"summary":"Register as a volunteer for an emergency (requires authentication)","tags":["volunteers"]},"get":{"operationId":"VolunteersController_getRosterForEmergency","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"skill","required":false,"in":"query","schema":{"enum":["driving","medical","logistics","cooking","languages","admin","general"],"type":"string"}},{"name":"availability","required":false,"in":"query","schema":{"enum":["immediate","this_week","flexible"],"type":"string"}},{"name":"vehicle","required":false,"in":"query","schema":{"enum":["none","car","van","truck"],"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"enum":["available","assigned","inactive"],"type":"string"}}],"responses":{"200":{"description":"List of volunteers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VolunteerViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"}},"security":[{"bearer":[]}],"summary":"Get volunteer roster for an emergency (coordinator only)","tags":["volunteers"]}},"/volunteers/{volunteerId}/status":{"post":{"operationId":"VolunteersController_updateVolunteerStatus","parameters":[{"name":"volunteerId","required":true,"in":"path","description":"Volunteer UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVolunteerStatusDto"}}}},"responses":{"204":{"description":"Status updated"},"400":{"description":"Invalid status or UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Volunteer not found"}},"security":[{"bearer":[]}],"summary":"Update volunteer status (coordinator of the volunteer's emergency only)","tags":["volunteers"]}},"/emergencies/{emergencyId}/volunteers/me":{"get":{"operationId":"VolunteersController_fetchMyProfile","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"My volunteer profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolunteerViewDto"}}}},"401":{"description":"Missing or invalid token"},"404":{"description":"Not registered as volunteer in this emergency"}},"security":[{"bearer":[]}],"summary":"Get my volunteer profile for an emergency","tags":["volunteers"]}},"/emergencies/{emergencyId}/tasks":{"post":{"operationId":"TasksController_createTask","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskDto"}}}},"responses":{"201":{"description":"Task created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskResponseDto"}}}},"400":{"description":"Invalid body or UUID"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"}},"security":[{"bearer":[]}],"summary":"Create a task for an emergency (coordinator only)","tags":["tasks"]},"get":{"operationId":"TasksController_getTasks","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}},{"name":"status","required":false,"in":"query","schema":{"enum":["open","in_progress","completed","cancelled"],"type":"string"}}],"responses":{"200":{"description":"Task list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskViewDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"}},"security":[{"bearer":[]}],"summary":"List tasks for an emergency (coordinator only)","tags":["tasks"]}},"/emergencies/{emergencyId}/tasks/mine":{"get":{"operationId":"TasksController_getMyTasks","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Tasks assigned to me","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyTaskViewDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"Get tasks assigned to the authenticated volunteer","tags":["tasks"]}},"/tasks/{taskId}/assign":{"post":{"operationId":"TasksController_assignVolunteer","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignVolunteerDto"}}}},"responses":{"204":{"description":"Volunteer assigned"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Task or volunteer not found"},"422":{"description":"Volunteer already assigned, wrong emergency, or task closed"}},"security":[{"bearer":[]}],"summary":"Assign a volunteer to a task (coordinator only)","tags":["tasks"]}},"/tasks/{taskId}/unassign":{"post":{"operationId":"TasksController_unassignVolunteer","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignVolunteerDto"}}}},"responses":{"204":{"description":"Volunteer unassigned"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Task not found"},"422":{"description":"Volunteer not assigned"}},"security":[{"bearer":[]}],"summary":"Unassign a volunteer from a task (coordinator only)","tags":["tasks"]}},"/tasks/{taskId}/check-in":{"post":{"operationId":"TasksController_checkIn","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignVolunteerDto"}}}},"responses":{"204":{"description":"Checked in"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the assigned volunteer or a coordinator"},"404":{"description":"Task not found"},"422":{"description":"Not assigned or already checked in"}},"security":[{"bearer":[]}],"summary":"Check in a volunteer to a task (volunteer themselves or coordinator)","tags":["tasks"]}},"/tasks/{taskId}/check-out":{"post":{"operationId":"TasksController_checkOut","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignVolunteerDto"}}}},"responses":{"204":{"description":"Checked out"},"401":{"description":"Missing or invalid token"},"403":{"description":"Only the assigned volunteer or a coordinator"},"404":{"description":"Task not found"},"422":{"description":"Not checked in"}},"security":[{"bearer":[]}],"summary":"Check out a volunteer from a task (volunteer themselves or coordinator)","tags":["tasks"]}},"/tasks/{taskId}/complete":{"post":{"operationId":"TasksController_completeTask","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Task completed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Task not found"},"422":{"description":"Task already cancelled"}},"security":[{"bearer":[]}],"summary":"Mark a task as completed (coordinator only)","tags":["tasks"]}},"/tasks/{taskId}/cancel":{"post":{"operationId":"TasksController_cancelTask","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Task cancelled"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Task not found"},"422":{"description":"Task already completed"}},"security":[{"bearer":[]}],"summary":"Cancel a task (coordinator only)","tags":["tasks"]}},"/files":{"post":{"operationId":"FilesController_upload","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"File stored successfully","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"},"url":{"type":"string"}}}}}}},"security":[{"bearer":[]}],"summary":"Upload an image file (max 5 MB)","tags":["files"]}},"/files/{key}":{"get":{"operationId":"FilesController_serve","parameters":[{"name":"key","required":true,"in":"path","description":"Storage key returned by POST /files","schema":{"type":"string"}}],"responses":{"200":{"description":"File content streamed"}},"security":[{"bearer":[]}],"summary":"Download a stored file by key (authenticated)","tags":["files"]}},"/emergencies/{emergencyId}/reports":{"post":{"operationId":"ReportsController_submit","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitReportDto"}}}},"responses":{"201":{"description":"Report submitted"}},"security":[{"bearer":[]}],"summary":"Submit a field report","tags":["reports"]},"get":{"operationId":"ReportsController_getQueue","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["open","reviewed","closed"]}},{"name":"priority","required":false,"in":"query","schema":{"type":"string","enum":["low","medium","high","urgent"]}},{"name":"resourceId","required":false,"in":"query","description":"Filter by resource ID","schema":{"type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by type","schema":{"type":"string","enum":["incident","stock","status","other"]}}],"responses":{"200":{"description":"List of reports"}},"security":[{"bearer":[]}],"summary":"Get coordination queue of reports","tags":["reports"]}},"/reports/{reportId}/review":{"post":{"operationId":"ReportsController_review","parameters":[{"name":"reportId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Report marked as reviewed"}},"security":[{"bearer":[]}],"summary":"Mark a report as reviewed","tags":["reports"]}},"/reports/{reportId}":{"patch":{"operationId":"ReportsController_edit","parameters":[{"name":"reportId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditReportDto"}}}},"responses":{"204":{"description":"Report edited"},"400":{"description":"Missing reason or report is closed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Report not found"}},"security":[{"bearer":[]}],"summary":"Editar un reporte durante el triaje (coordinador). Requiere un motivo; se registra en la trazabilidad.","tags":["reports"]}},"/reports/{reportId}/discard":{"post":{"operationId":"ReportsController_discard","parameters":[{"name":"reportId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscardReportDto"}}}},"responses":{"204":{"description":"Report discarded (closed)"},"400":{"description":"Missing reason or report already closed"},"401":{"description":"Missing or invalid token"},"403":{"description":"Coordinator role required"},"404":{"description":"Report not found"}},"security":[{"bearer":[]}],"summary":"Descartar un reporte durante el triaje (coordinador). Requiere un motivo; se registra en la trazabilidad.","tags":["reports"]}},"/emergencies/{emergencyId}/reports/mine":{"get":{"operationId":"ReportsController_mine","parameters":[{"name":"emergencyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"List of my reports"}},"security":[{"bearer":[]}],"summary":"Get my field reports for an emergency","tags":["reports"]}},"/audit":{"get":{"operationId":"AuditController_list","parameters":[{"name":"emergencyId","required":false,"in":"query","description":"Filter by emergency ID","schema":{"type":"string"}},{"name":"actorUserId","required":false,"in":"query","description":"Filter by actor user ID","schema":{"type":"string"}},{"name":"entityType","required":false,"in":"query","description":"Filter by entity type (e.g. resource, need)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max results to return (default 100, max 500)","schema":{"default":100,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset for pagination (default 0)","schema":{"default":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditListResponseDto"}}}},"401":{"description":"Unauthorized"},"403":{"description":"audit:read required"}},"security":[{"bearer":[]}],"summary":"List audit log entries (audit:read)","tags":["audit"]}},"/emergencies/{emergencyId}/audit":{"get":{"operationId":"EmergencyAuditController_list","parameters":[{"name":"emergencyId","required":true,"in":"path","description":"Emergency UUID","schema":{"format":"uuid","type":"string"}},{"name":"actorUserId","required":false,"in":"query","description":"Filter by actor user ID","schema":{"type":"string"}},{"name":"entityType","required":false,"in":"query","description":"Filter by entity type (e.g. resource, need)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max results to return (default 100, max 500)","schema":{"default":100,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset for pagination (default 0)","schema":{"default":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditListResponseDto"}}}},"401":{"description":"Unauthorized"},"403":{"description":"audit:read required (coordinator of this emergency)"}},"security":[{"bearer":[]}],"summary":"Activity trail for an emergency — coordinators of the emergency only","tags":["audit"]}},"/groups/mine":{"get":{"operationId":"GroupsController_mine","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyGroupResponseDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List the groups I belong to (any status)","tags":["groups"]}},"/groups":{"get":{"operationId":"GroupsController_list","parameters":[{"name":"ownerKind","required":true,"in":"query","schema":{"type":"string","enum":["organization","emergency"]}},{"name":"ownerId","required":true,"in":"query","description":"Id of the owning org/emergency","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GroupResponseDto"}}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"List groups under an org/emergency (all if you can read; else public)","tags":["groups"]},"post":{"operationId":"GroupsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"group:create required in the owner scope"}},"security":[{"bearer":[]}],"summary":"Create a group (creator becomes its first manager)","tags":["groups"]}},"/groups/{groupId}":{"get":{"operationId":"GroupsController_getOne","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupResponseDto"}}}},"401":{"description":"Missing or invalid token"}},"security":[{"bearer":[]}],"summary":"Get a group’s basic info","tags":["groups"]}},"/groups/{groupId}/join":{"post":{"operationId":"GroupsController_join","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Join request registered"},"401":{"description":"Missing or invalid token"},"403":{"description":"Group is private"}},"security":[{"bearer":[]}],"summary":"Request to join a public group (awaits approval)","tags":["groups"]}},"/groups/{groupId}/members":{"get":{"operationId":"GroupsController_members","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GroupMemberResponseDto"}}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"group:read required"}},"security":[{"bearer":[]}],"summary":"List a group’s members","tags":["groups"]},"post":{"operationId":"GroupsController_addMember","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberByEmailDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupMemberResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"group:manage_members required"}},"security":[{"bearer":[]}],"summary":"Add a member by email (manager only)","tags":["groups"]}},"/groups/{groupId}/members/{userId}/approve":{"post":{"operationId":"GroupsController_approve","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Member approved"},"401":{"description":"Missing or invalid token"},"403":{"description":"group:manage_members required"}},"security":[{"bearer":[]}],"summary":"Approve a pending member (manager only)","tags":["groups"]}},"/groups/{groupId}/managers":{"post":{"operationId":"GroupsController_assignManager","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignManagerDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdResponseDto"}}}},"401":{"description":"Missing or invalid token"},"403":{"description":"role:grant required, or privilege escalation"}},"security":[{"bearer":[]}],"summary":"Appoint a member as co-manager (delegated, attenuated)","tags":["groups"]}}},"info":{"title":"ResponseGrid API","description":"API for humanitarian emergency resource coordination","version":"0.1","contact":{}},"tags":[{"name":"resources","description":""}],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"},"api-key":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"LoginDto":{"type":"object","properties":{"email":{"type":"string","example":"admin@reliefhub.org"},"password":{"type":"string","example":"admin1234","minLength":6}},"required":["email","password"]},"LoginResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","description":"JWT access token"}},"required":["accessToken"]},"RegisterDto":{"type":"object","properties":{"email":{"type":"string","example":"user@reliefhub.org"},"password":{"type":"string","example":"password123","minLength":8},"name":{"type":"string","example":"Jane Doe"},"phone":{"type":"string","example":"+58 412 555 0101","description":"Teléfono de contacto (obligatorio)"},"acceptedTerms":{"type":"boolean","example":true,"description":"Aceptación de las condiciones del servicio (debe ser true)"},"acceptedPrivacy":{"type":"boolean","example":true,"description":"Aceptación de la política de privacidad (debe ser true)"}},"required":["email","password","name","phone","acceptedTerms","acceptedPrivacy"]},"RegisterResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","description":"JWT access token (auto-login after registration)"}},"required":["accessToken"]},"OnboardingDto":{"type":"object","properties":{"phone":{"type":"string","example":"+58 412 555 0101","description":"Teléfono de contacto (obligatorio)"},"acceptedTerms":{"type":"boolean","example":true,"description":"Aceptación de las condiciones del servicio (debe ser true)"},"acceptedPrivacy":{"type":"boolean","example":true,"description":"Aceptación de la política de privacidad (debe ser true)"}},"required":["phone","acceptedTerms","acceptedPrivacy"]},"OnboardingResponseDto":{"type":"object","properties":{"profileComplete":{"type":"boolean","description":"true una vez el perfil queda completo (teléfono + consentimientos)"}},"required":["profileComplete"]},"MeGrantDto":{"type":"object","properties":{"roleId":{"type":"string","description":"Role id from the catalog","example":"org_admin"},"scopeType":{"type":"string","enum":["platform","organization","emergency","group","entity"]},"scopeId":{"type":"string","nullable":true,"description":"Scope id (null for platform)"},"expiresAt":{"type":"string","nullable":true,"description":"ISO expiry, or null"}},"required":["roleId","scopeType","scopeId","expiresAt"]},"MeResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"User UUID"},"email":{"type":"string","example":"user@example.com"},"name":{"type":"string","example":"Jane Doe"},"isAdmin":{"type":"boolean"},"phone":{"type":"string","example":"+58 412 555 0101","nullable":true,"description":"Optional contact phone, null until the user provides one"},"profileComplete":{"type":"boolean","description":"true si el perfil está completo (teléfono + consentimientos vigentes). false obliga a pasar por el onboarding (típico en altas sociales)."},"grants":{"description":"The effective role grants (role @ scope) for this user","type":"array","items":{"$ref":"#/components/schemas/MeGrantDto"}}},"required":["id","email","name","isAdmin","phone","profileComplete","grants"]},"UpdateProfileDto":{"type":"object","properties":{"phone":{"type":"string","example":"+58 412 555 0101","nullable":true,"description":"Nuevo teléfono. null para borrar."},"name":{"type":"string","example":"Nuevo Nombre"}}},"LoginByPhoneDto":{"type":"object","properties":{"phone":{"type":"string","example":"+58 412 555 0101","description":"Teléfono verificado por el canal de confianza (bot). Se normaliza (espacios, guiones, prefijo +) antes de buscar la cuenta."}},"required":["phone"]},"TrustedAuthUserDto":{"type":"object","properties":{"id":{"type":"string","description":"User UUID"},"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane@example.com"}},"required":["id","name","email"]},"TrustedAuthResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","description":"JWT del usuario — mismos claims que /auth/login; nunca confiere más permisos que los del propio usuario."},"user":{"$ref":"#/components/schemas/TrustedAuthUserDto"}},"required":["accessToken","user"]},"RegisterByPhoneDto":{"type":"object","properties":{"phone":{"type":"string","example":"+58 412 555 0101"},"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane@example.com"},"acceptedTerms":{"type":"boolean","example":true,"description":"Aceptación de las condiciones del servicio (debe ser true; el bot la recogió en la conversación)."},"acceptedPrivacy":{"type":"boolean","example":true,"description":"Aceptación de la política de privacidad (debe ser true)."}},"required":["phone","name","email","acceptedTerms","acceptedPrivacy"]},"GrantListItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"principalId":{"type":"string","format":"uuid"},"principalType":{"type":"string","enum":["user","service_account"]},"roleId":{"type":"string"},"scopeType":{"type":"string"},"scopeId":{"type":"string","nullable":true},"grantedByPrincipalId":{"type":"string","nullable":true},"grantedAt":{"type":"string"},"expiresAt":{"type":"string","nullable":true},"principalName":{"type":"string","nullable":true,"description":"Display name of the principal (when resolvable)"},"principalEmail":{"type":"string","nullable":true,"description":"Email of the principal (users only)"}},"required":["id","principalId","principalType","roleId","scopeType","scopeId","grantedByPrincipalId","grantedAt","expiresAt","principalName","principalEmail"]},"GrantRoleDto":{"type":"object","properties":{"principalId":{"type":"string","format":"uuid","description":"Principal receiving the role"},"principalType":{"type":"string","enum":["user","service_account"],"description":"Kind of principal receiving the role (defaults to user). Set to service_account when granting to a machine principal so it resolves and labels correctly."},"roleId":{"type":"string","description":"Role id from the fixed catalog (e.g. emergency_coordinator)"},"scopeType":{"type":"string","enum":["platform","organization","emergency","group","entity"],"description":"Scope the role applies to"},"scopeId":{"type":"string","description":"Scope id (required for every scope except platform)"},"scopeEntityType":{"type":"string","description":"Entity type (required for entity scope)"},"expiresAt":{"type":"string","description":"ISO 8601 expiry — for temporary / break-glass grants"}},"required":["principalId","roleId","scopeType"]},"GrantResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]},"ServiceAccountListItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"ownerOrganizationId":{"type":"object","format":"uuid","nullable":true},"createdByUserId":{"type":"string","format":"uuid"},"createdAt":{"type":"string"}},"required":["id","name","ownerOrganizationId","createdByUserId","createdAt"]},"ApiKeyListItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string","description":"Non-secret lookup prefix"},"active":{"type":"boolean"},"expiresAt":{"type":"string","nullable":true},"lastUsedAt":{"type":"string","nullable":true},"revokedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"}},"required":["id","prefix","active","expiresAt","lastUsedAt","revokedAt","createdAt"]},"ServiceAccountGrantDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roleId":{"type":"string"},"scopeType":{"type":"string"},"scopeId":{"type":"string","nullable":true},"grantedAt":{"type":"string"},"expiresAt":{"type":"string","nullable":true}},"required":["id","roleId","scopeType","scopeId","grantedAt","expiresAt"]},"CreateServiceAccountDto":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name for the service account"},"ownerOrganizationId":{"type":"string","format":"uuid","description":"Owning organization; omit for a platform-level account"}},"required":["name"]},"ServiceAccountResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]},"IssueApiKeyDto":{"type":"object","properties":{"expiresAt":{"type":"string","description":"ISO 8601 expiry for the key"}}},"IssuedApiKeyResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"apiKey":{"type":"string","description":"The full secret key — shown once; store it now"},"prefix":{"type":"string"}},"required":["id","apiKey","prefix"]},"IntrospectedGrantDto":{"type":"object","properties":{"roleId":{"type":"string"},"scopeType":{"type":"string"},"scopeId":{"type":"object","nullable":true}},"required":["roleId","scopeType","scopeId"]},"ServiceAccountIdentityDto":{"type":"object","properties":{"principalId":{"type":"string","format":"uuid"},"principalType":{"type":"string","enum":["service_account"]},"grants":{"type":"array","items":{"$ref":"#/components/schemas/IntrospectedGrantDto"}}},"required":["principalId","principalType","grants"]},"RoleDto":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"defaultScopeType":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"}}},"required":["id","description","defaultScopeType","permissions"]},"UserLookupDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"name":{"type":"string"}},"required":["id","email","name"]},"UserAdminListItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"name":{"type":"string"},"isAdmin":{"type":"boolean"},"createdAt":{"type":"string","description":"ISO 8601 registration date"},"lastLoginAt":{"type":"string","nullable":true,"description":"ISO 8601 last login, or null if never logged in"},"roles":{"description":"Distinct roles the user holds across every scope","type":"array","items":{"type":"string"}},"grantCount":{"type":"number","description":"Number of active grants held"}},"required":["id","email","name","isAdmin","createdAt","lastLoginAt","roles","grantCount"]},"UserGrantDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roleId":{"type":"string"},"scopeType":{"type":"string"},"scopeId":{"type":"string","nullable":true},"scopeName":{"type":"string","nullable":true,"description":"Display name of the scope (org/emergency/group), when resolvable"},"grantedByPrincipalId":{"type":"string","nullable":true},"grantedAt":{"type":"string"},"expiresAt":{"type":"string","nullable":true}},"required":["id","roleId","scopeType","scopeId","scopeName","grantedByPrincipalId","grantedAt","expiresAt"]},"UserOrganizationDto":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"organizationName":{"type":"string"},"role":{"type":"string"}},"required":["organizationId","organizationName","role"]},"UserActivityDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string"},"entityType":{"type":"string","nullable":true},"entityId":{"type":"string","nullable":true},"emergencyId":{"type":"string","nullable":true},"method":{"type":"string"},"path":{"type":"string"},"statusCode":{"type":"number"},"createdAt":{"type":"string","description":"ISO 8601 timestamp"}},"required":["id","action","entityType","entityId","emergencyId","method","path","statusCode","createdAt"]},"UserAdminDetailDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"name":{"type":"string"},"isAdmin":{"type":"boolean"},"createdAt":{"type":"string","description":"ISO 8601 registration date"},"lastLoginAt":{"type":"string","nullable":true},"grants":{"type":"array","items":{"$ref":"#/components/schemas/UserGrantDto"}},"organizations":{"type":"array","items":{"$ref":"#/components/schemas/UserOrganizationDto"}},"activity":{"type":"array","items":{"$ref":"#/components/schemas/UserActivityDto"}}},"required":["id","email","name","isAdmin","createdAt","lastLoginAt","grants","organizations","activity"]},"NotificationDto":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"emergencyId":{"type":"string","nullable":true},"type":{"type":"string","enum":["resource_verified","offer_matched","task_assigned","donation_received"]},"message":{"type":"string"},"link":{"type":"string","nullable":true},"read":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","userId","emergencyId","type","message","link","read","createdAt"]},"GetMyNotificationsResponseDto":{"type":"object","properties":{"notifications":{"type":"array","items":{"$ref":"#/components/schemas/NotificationDto"}},"unreadCount":{"type":"number"}},"required":["notifications","unreadCount"]},"LocationDto":{"type":"object","properties":{"address":{"type":"string","example":"Calle Mayor 1, Valencia"},"latitude":{"type":"number","example":39.4699,"description":"Latitude between -90 and 90"},"longitude":{"type":"number","example":-0.3763,"description":"Longitude between -180 and 180"}},"required":["address","latitude","longitude"]},"SupplyLineDto":{"type":"object","properties":{"name":{"type":"string","example":"Water bottles","description":"Name of the supply"},"supplyId":{"type":"string","example":"1e4b5f3b-5c9c-4f77-8f50-3d2dbdc0c7d8","nullable":true,"description":"Optional soft link to the canonical supply master data."},"quantity":{"type":"number","example":100,"description":"Quantity (positive integer)"},"unit":{"type":"string","example":"liters","description":"Unit of measurement (optional)"},"category":{"type":"string","example":"water","description":"Slug de categoría de material (data-driven, lowercase snake_case). Los slugs core son: food, water, hygiene, clothing, medical, shelter, tools, other, medicines, medical_equipment, medical_supplies, medical_personnel, food_fresh, food_non_perishable, hygiene_infantile, hygiene_personal, tools_extraction, other_pets. El conjunto es abierto: la taxonomía puede crecer vía datos (tabla `categories`)."},"presentation":{"type":"string","example":"ampolla","description":"Presentation / route of administration: ampolla, EV (intravenoso), inhalador, pastilla, jarabe… Optional, free-form (#61)."},"expiresAt":{"type":"string","example":"2026-07-01","format":"date","description":"Optional freshness date for the line, expressed as an ISO date (YYYY-MM-DD)."}},"required":["name","quantity","category"]},"AuthorDto":{"type":"object","properties":{"name":{"type":"string","example":"María P.","maxLength":200},"email":{"type":"string","example":"maria@example.com","maxLength":320},"phone":{"type":"string","example":"+58 412 1234567","maxLength":64},"note":{"type":"string","example":"Contactar por la tarde; vive en el 3er piso","maxLength":2000,"description":"Free-text contact / context note"},"verified":{"type":"boolean","example":false,"default":false,"description":"Whether the integrating source verified this identity (e.g. OTP). Defaults to false — public anonymous capture is unverified."},"source":{"type":"string","example":"terremotovenezuela.app","maxLength":200,"description":"Which integration contributed this author"}}},"RegisterResourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid","minLength":2},"description":{"type":"string","example":"Centro de acopio principal"},"location":{"description":"Physical location of the resource","allOf":[{"$ref":"#/components/schemas/LocationDto"}]},"ownerOrganizationId":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6","description":"Organization offering this resource (optional)"},"contact":{"type":"string","example":"+58 212 555 0000","description":"Contact info for this resource point"},"schedule":{"type":"string","example":"Lun-Vie 08-18","description":"Operating schedule"},"manager":{"type":"string","example":"Juan Pérez","description":"Responsible manager name"},"accepts":{"example":["water","food"],"description":"Category slugs this resource accepts","type":"array","items":{"type":"string"}},"country":{"type":"string","example":"VE","description":"ISO 3166-1 alpha-2 country code"},"city":{"type":"string","example":"Caracas","description":"City name"},"capacity":{"type":"number","example":200,"description":"Total capacity of the place (e.g. shelter beds)"},"occupancy":{"type":"number","example":120,"description":"Current occupancy of the place"},"sourceOrganisation":{"type":"string","example":"Protección Civil","description":"Organisation that is the source of this record"},"sourceUrl":{"type":"string","example":"https://example.org/refugios/123","description":"URL of the source record"},"isFinalRecipient":{"type":"boolean","example":true,"description":"Mark this resource as a final recipient of aid"},"recipientType":{"type":"string","example":"hospital","description":"Recipient type slug (see the emergency recipient-type taxonomy)"},"items":{"description":"Declared inventory: the supply lines this place holds for delivery (optional)","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"author":{"description":"Restricted contact of the real person this place is registered on behalf of (#235). Optional; required when a trusted integration writes via API key. Never exposed on public reads.","allOf":[{"$ref":"#/components/schemas/AuthorDto"}]}},"required":["type","name","location"]},"RegisterResourceResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"required":["id"]},"RecordInventoryEntryDto":{"type":"object","properties":{"items":{"description":"Supply lines received into the point stock (at least one)","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}}},"required":["items"]},"SupplyLineResponseDto":{"type":"object","properties":{"name":{"type":"string","example":"Water bottles"},"supplyId":{"type":"string","example":"1e4b5f3b-5c9c-4f77-8f50-3d2dbdc0c7d8","nullable":true,"description":"Optional soft link to the canonical supply master data."},"quantity":{"type":"number","example":100},"unit":{"type":"string","example":"liters","nullable":true},"category":{"type":"string","example":"water","description":"Slug de categoría de material (data-driven, lowercase snake_case)."},"presentation":{"type":"string","example":"ampolla","nullable":true,"description":"Presentation / route of administration (ampolla, EV, inhalador…) — #61."},"expiresAt":{"type":"string","example":"2026-07-01","nullable":true,"format":"date","description":"Optional freshness date for the line, expressed as an ISO date (YYYY-MM-DD)."}},"required":["name","supplyId","quantity","category"]},"UpdateInventoryDto":{"type":"object","properties":{"items":{"description":"Full declared inventory (replaces current; empty clears it)","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}}},"required":["items"]},"VerifyResourceDto":{"type":"object","properties":{}},"EditResourceDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo de la edición (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Se corrige el nombre y se completa el horario"},"name":{"type":"string","description":"Nuevo nombre (omitir para no cambiarlo)","minLength":2},"description":{"type":"string","description":"Nueva descripción. Cadena vacía la borra. Omitir para no cambiarla.","nullable":true},"contact":{"type":"string","description":"Nuevo contacto. Cadena vacía lo borra. Omitir para no cambiarlo.","nullable":true},"schedule":{"type":"string","description":"Nuevo horario. Cadena vacía lo borra. Omitir para no cambiarlo.","nullable":true},"capacity":{"type":"number","description":"Nueva capacidad total. Omitir para no cambiarla.","example":200},"occupancy":{"type":"number","description":"Nueva ocupación actual. Omitir para no cambiarla.","example":120},"sourceOrganisation":{"type":"string","description":"Nueva organización de origen. Cadena vacía la borra. Omitir para no cambiarla.","nullable":true},"sourceUrl":{"type":"string","description":"Nueva URL de origen. Cadena vacía la borra. Omitir para no cambiarla.","nullable":true},"location":{"description":"Nueva ubicación (dirección + coordenadas) para corregir la geolocalización. Omitir para no cambiarla.","allOf":[{"$ref":"#/components/schemas/LocationDto"}]}},"required":["reason"]},"DiscardResourceDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo del descarte (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Punto duplicado / fuera del ámbito de la emergencia"}},"required":["reason"]},"UpdateResourcePublicStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["active","saturated","paused","closed","preparing","assisted"],"example":"saturated","description":"Target operational status. Hidden is not allowed; use close() to deactivate."}},"required":["status"]},"MyManagedResourceDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid"},"emergencyId":{"type":"string","format":"uuid"},"emergencySlug":{"type":"string","example":"terremoto-venezuela-2026","nullable":true,"description":"Slug of the owning emergency; null if its row is missing."}},"required":["id","type","name","emergencyId","emergencySlug"]},"LocationViewDto":{"type":"object","properties":{"address":{"type":"string","example":"Calle Mayor 1, Valencia"},"latitude":{"type":"number","example":39.4699},"longitude":{"type":"number","example":-0.3763}},"required":["address","latitude","longitude"]},"ResourceViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid"},"description":{"type":"string","example":"Centro de acopio principal","nullable":true},"location":{"$ref":"#/components/schemas/LocationViewDto"},"verificationLevel":{"type":"string","enum":["unverified","verified","official","rejected"],"example":"verified"},"publicStatus":{"type":"string","enum":["hidden","active","saturated","paused","closed","preparing","assisted"],"example":"active"},"ownerOrganizationId":{"type":"string","format":"uuid","nullable":true},"accepts":{"example":["water","food"],"type":"array","items":{"type":"string"}},"contact":{"type":"string","example":"+58 212 555 0000","nullable":true,"description":"Contact line. Personal data: nulled for anonymous callers on non-official resources — authenticate to reveal it. Use `hasContact` to tell \"hidden\" from \"absent\"."},"hasContact":{"type":"boolean","example":true,"description":"Whether a contact exists on file, regardless of whether `contact` is revealed to this caller."},"schedule":{"type":"string","example":"Lun-Vie 08-18","nullable":true},"manager":{"type":"string","example":"Juan Pérez","nullable":true},"sourceName":{"type":"string","example":"acopiove.org","nullable":true},"externalUpdatedAt":{"type":"string","example":"2026-06-27T00:00:00.000Z","description":"ISO 8601 date string","nullable":true},"country":{"type":"string","example":"Venezuela","description":"Country string as stored by the ingestion source (e.g. full Spanish name \"Venezuela\"). NOT guaranteed to be an ISO 3166-1 alpha-2 code — value depends on the source `pais` field.","nullable":true},"city":{"type":"string","example":"Caracas","nullable":true},"capacity":{"type":"number","example":200,"nullable":true,"description":"Total capacity of the place (e.g. shelter beds)"},"occupancy":{"type":"number","example":120,"nullable":true,"description":"Current occupancy of the place"},"sourceOrganisation":{"type":"string","example":"Protección Civil","nullable":true,"description":"Organisation that is the source of this record"},"sourceUrl":{"type":"string","example":"https://example.org/refugios/123","nullable":true,"description":"URL of the source record"},"isFinalRecipient":{"type":"boolean","example":false,"description":"Whether this resource is a final recipient of aid"},"recipientType":{"type":"string","example":"hospital","nullable":true,"description":"Recipient type slug (see the emergency recipient-type taxonomy)"},"disputed":{"type":"boolean","example":false,"description":"Whether enough citizens have reported this point as invalid; it stays visible with an \"in review\" warning until a coordinator resolves it."},"disputedAt":{"type":"string","example":"2026-06-28T12:00:00.000Z","nullable":true,"description":"When the point was flagged as disputed (ISO 8601), or null"}},"required":["id","type","name","description","location","verificationLevel","publicStatus","ownerOrganizationId","accepts","contact","hasContact","schedule","manager","sourceName","externalUpdatedAt","country","city","capacity","occupancy","sourceOrganisation","sourceUrl","isFinalRecipient","recipientType","disputed","disputedAt"]},"ReportResourceValidityDto":{"type":"object","properties":{"reason":{"type":"string","enum":["closed","nonexistent","moved","outdated"],"example":"closed","description":"Motivo: cerrado / ya no existe / se ha mudado / datos desactualizados"},"note":{"type":"string","description":"Detalle opcional aportado por el ciudadano","maxLength":1000},"photoUrls":{"description":"URLs de fotos de evidencia (subidas vía /files)","type":"array","items":{"type":"string"}}},"required":["reason"]},"ReportResourceValidityResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"disputed":{"type":"boolean","example":false,"description":"Whether this report pushed the resource over the threshold to disputed"}},"required":["id","disputed"]},"ResolveResourceDisputeDto":{"type":"object","properties":{"resolution":{"type":"string","enum":["confirm_closed","mark_invalid","dismiss"],"example":"confirm_closed","description":"confirm_closed = cerrar (reversible) · mark_invalid = marcar inválido (rejected, para \"ya no existe\") · dismiss = descartar (sigue activo)"},"reason":{"type":"string","description":"Motivo de la resolución (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Confirmado por teléfono con el responsable: el punto cerró."}},"required":["resolution","reason"]},"ValidityReportDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"resourceId":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"reporterUserId":{"type":"string","format":"uuid"},"reason":{"type":"string","enum":["closed","nonexistent","moved","outdated"],"example":"closed"},"note":{"type":"string","nullable":true},"photoUrls":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["open","accepted","dismissed"],"example":"open"},"createdAt":{"type":"string","format":"date-time"},"resolvedByUserId":{"type":"string","format":"uuid","nullable":true},"resolvedAt":{"type":"string","format":"date-time","nullable":true}},"required":["id","resourceId","emergencyId","reporterUserId","reason","note","photoUrls","status","createdAt","resolvedByUserId","resolvedAt"]},"PagedResourcesDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ResourceViewDto"}},"total":{"type":"number","example":123},"page":{"type":"number","example":1},"limit":{"type":"number","example":50}},"required":["items","total","page","limit"]},"DisputedResourceDto":{"type":"object","properties":{"resource":{"$ref":"#/components/schemas/ResourceViewDto"},"distinctReporters":{"type":"number","example":3,"description":"Distinct citizens with an open report on this resource"},"byReason":{"type":"object","additionalProperties":{"type":"number"},"example":{"closed":2,"moved":1},"description":"Open-report counts keyed by reason"},"lastReportedAt":{"type":"string","format":"date-time","nullable":true}},"required":["resource","distinctReporters","byReason","lastReportedAt"]},"NearbyResourceViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid"},"description":{"type":"string","example":"Centro de acopio principal","nullable":true},"location":{"$ref":"#/components/schemas/LocationViewDto"},"verificationLevel":{"type":"string","enum":["unverified","verified","official","rejected"],"example":"verified"},"publicStatus":{"type":"string","enum":["hidden","active","saturated","paused","closed","preparing","assisted"],"example":"active"},"ownerOrganizationId":{"type":"string","format":"uuid","nullable":true},"accepts":{"example":["water","food"],"type":"array","items":{"type":"string"}},"contact":{"type":"string","example":"+58 212 555 0000","nullable":true,"description":"Contact line. Personal data: nulled for anonymous callers on non-official resources — authenticate to reveal it. Use `hasContact` to tell \"hidden\" from \"absent\"."},"hasContact":{"type":"boolean","example":true,"description":"Whether a contact exists on file, regardless of whether `contact` is revealed to this caller."},"schedule":{"type":"string","example":"Lun-Vie 08-18","nullable":true},"manager":{"type":"string","example":"Juan Pérez","nullable":true},"sourceName":{"type":"string","example":"acopiove.org","nullable":true},"externalUpdatedAt":{"type":"string","example":"2026-06-27T00:00:00.000Z","description":"ISO 8601 date string","nullable":true},"country":{"type":"string","example":"Venezuela","description":"Country string as stored by the ingestion source (e.g. full Spanish name \"Venezuela\"). NOT guaranteed to be an ISO 3166-1 alpha-2 code — value depends on the source `pais` field.","nullable":true},"city":{"type":"string","example":"Caracas","nullable":true},"capacity":{"type":"number","example":200,"nullable":true,"description":"Total capacity of the place (e.g. shelter beds)"},"occupancy":{"type":"number","example":120,"nullable":true,"description":"Current occupancy of the place"},"sourceOrganisation":{"type":"string","example":"Protección Civil","nullable":true,"description":"Organisation that is the source of this record"},"sourceUrl":{"type":"string","example":"https://example.org/refugios/123","nullable":true,"description":"URL of the source record"},"isFinalRecipient":{"type":"boolean","example":false,"description":"Whether this resource is a final recipient of aid"},"recipientType":{"type":"string","example":"hospital","nullable":true,"description":"Recipient type slug (see the emergency recipient-type taxonomy)"},"disputed":{"type":"boolean","example":false,"description":"Whether enough citizens have reported this point as invalid; it stays visible with an \"in review\" warning until a coordinator resolves it."},"disputedAt":{"type":"string","example":"2026-06-28T12:00:00.000Z","nullable":true,"description":"When the point was flagged as disputed (ISO 8601), or null"},"distanceMeters":{"type":"number","example":1234,"description":"Distance from query point in meters (rounded)"}},"required":["id","type","name","description","location","verificationLevel","publicStatus","ownerOrganizationId","accepts","contact","hasContact","schedule","manager","sourceName","externalUpdatedAt","country","city","capacity","occupancy","sourceOrganisation","sourceUrl","isFinalRecipient","recipientType","disputed","disputedAt","distanceMeters"]},"NearbyResourcesResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/NearbyResourceViewDto"}}},"required":["items"]},"InBoundsResourcesDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ResourceViewDto"}}},"required":["items"]},"ResourceFacetsDto":{"type":"object","properties":{"byCategory":{"type":"object","example":{"water":5,"food":3}},"byCountry":{"type":"object","example":{"Venezuela":3,"Colombia":2},"description":"Counts keyed by the stored `country` string. Values mirror the ingestion source `pais` field (full Spanish names, NOT ISO 3166-1 alpha-2 codes)."},"total":{"type":"number","example":8}},"required":["byCategory","byCountry","total"]},"ResourceDetailViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid"},"description":{"type":"string","example":"Centro de acopio principal","nullable":true},"location":{"$ref":"#/components/schemas/LocationViewDto"},"verificationLevel":{"type":"string","enum":["unverified","verified","official","rejected"],"example":"verified"},"publicStatus":{"type":"string","enum":["hidden","active","saturated","paused","closed","preparing","assisted"],"example":"active"},"ownerOrganizationId":{"type":"string","format":"uuid","nullable":true},"accepts":{"example":["water","food"],"type":"array","items":{"type":"string"}},"contact":{"type":"string","example":"+58 212 555 0000","nullable":true,"description":"Contact line. Personal data: nulled for anonymous callers on non-official resources — authenticate to reveal it. Use `hasContact` to tell \"hidden\" from \"absent\"."},"hasContact":{"type":"boolean","example":true,"description":"Whether a contact exists on file, regardless of whether `contact` is revealed to this caller."},"schedule":{"type":"string","example":"Lun-Vie 08-18","nullable":true},"manager":{"type":"string","example":"Juan Pérez","nullable":true},"sourceName":{"type":"string","example":"acopiove.org","nullable":true},"externalUpdatedAt":{"type":"string","example":"2026-06-27T00:00:00.000Z","description":"ISO 8601 date string","nullable":true},"country":{"type":"string","example":"Venezuela","description":"Country string as stored by the ingestion source (e.g. full Spanish name \"Venezuela\"). NOT guaranteed to be an ISO 3166-1 alpha-2 code — value depends on the source `pais` field.","nullable":true},"city":{"type":"string","example":"Caracas","nullable":true},"capacity":{"type":"number","example":200,"nullable":true,"description":"Total capacity of the place (e.g. shelter beds)"},"occupancy":{"type":"number","example":120,"nullable":true,"description":"Current occupancy of the place"},"sourceOrganisation":{"type":"string","example":"Protección Civil","nullable":true,"description":"Organisation that is the source of this record"},"sourceUrl":{"type":"string","example":"https://example.org/refugios/123","nullable":true,"description":"URL of the source record"},"isFinalRecipient":{"type":"boolean","example":false,"description":"Whether this resource is a final recipient of aid"},"recipientType":{"type":"string","example":"hospital","nullable":true,"description":"Recipient type slug (see the emergency recipient-type taxonomy)"},"disputed":{"type":"boolean","example":false,"description":"Whether enough citizens have reported this point as invalid; it stays visible with an \"in review\" warning until a coordinator resolves it."},"disputedAt":{"type":"string","example":"2026-06-28T12:00:00.000Z","nullable":true,"description":"When the point was flagged as disputed (ISO 8601), or null"},"inventoryCategories":{"example":["water","hygiene"],"description":"Distinct categories of material this place has declared","type":"array","items":{"type":"string"}}},"required":["id","type","name","description","location","verificationLevel","publicStatus","ownerOrganizationId","accepts","contact","hasContact","schedule","manager","sourceName","externalUpdatedAt","country","city","capacity","occupancy","sourceOrganisation","sourceUrl","isFinalRecipient","recipientType","disputed","disputedAt","inventoryCategories"]},"AuthorResponseDto":{"type":"object","properties":{"name":{"type":"string","nullable":true,"example":"María P."},"email":{"type":"string","nullable":true,"example":"maria@example.com"},"phone":{"type":"string","nullable":true,"example":"+58…"},"note":{"type":"string","nullable":true},"verified":{"type":"boolean","example":false},"source":{"type":"string","nullable":true,"example":"terremotovenezuela.app"}},"required":["verified"]},"ResourceAdminViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid"},"description":{"type":"string","example":"Centro de acopio principal","nullable":true},"location":{"$ref":"#/components/schemas/LocationViewDto"},"verificationLevel":{"type":"string","enum":["unverified","verified","official","rejected"],"example":"verified"},"publicStatus":{"type":"string","enum":["hidden","active","saturated","paused","closed","preparing","assisted"],"example":"active"},"ownerOrganizationId":{"type":"string","format":"uuid","nullable":true},"accepts":{"example":["water","food"],"type":"array","items":{"type":"string"}},"contact":{"type":"string","example":"+58 212 555 0000","nullable":true,"description":"Contact line. Personal data: nulled for anonymous callers on non-official resources — authenticate to reveal it. Use `hasContact` to tell \"hidden\" from \"absent\"."},"hasContact":{"type":"boolean","example":true,"description":"Whether a contact exists on file, regardless of whether `contact` is revealed to this caller."},"schedule":{"type":"string","example":"Lun-Vie 08-18","nullable":true},"manager":{"type":"string","example":"Juan Pérez","nullable":true},"sourceName":{"type":"string","example":"acopiove.org","nullable":true},"externalUpdatedAt":{"type":"string","example":"2026-06-27T00:00:00.000Z","description":"ISO 8601 date string","nullable":true},"country":{"type":"string","example":"Venezuela","description":"Country string as stored by the ingestion source (e.g. full Spanish name \"Venezuela\"). NOT guaranteed to be an ISO 3166-1 alpha-2 code — value depends on the source `pais` field.","nullable":true},"city":{"type":"string","example":"Caracas","nullable":true},"capacity":{"type":"number","example":200,"nullable":true,"description":"Total capacity of the place (e.g. shelter beds)"},"occupancy":{"type":"number","example":120,"nullable":true,"description":"Current occupancy of the place"},"sourceOrganisation":{"type":"string","example":"Protección Civil","nullable":true,"description":"Organisation that is the source of this record"},"sourceUrl":{"type":"string","example":"https://example.org/refugios/123","nullable":true,"description":"URL of the source record"},"isFinalRecipient":{"type":"boolean","example":false,"description":"Whether this resource is a final recipient of aid"},"recipientType":{"type":"string","example":"hospital","nullable":true,"description":"Recipient type slug (see the emergency recipient-type taxonomy)"},"disputed":{"type":"boolean","example":false,"description":"Whether enough citizens have reported this point as invalid; it stays visible with an \"in review\" warning until a coordinator resolves it."},"disputedAt":{"type":"string","example":"2026-06-28T12:00:00.000Z","nullable":true,"description":"When the point was flagged as disputed (ISO 8601), or null"},"emergencyId":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111","description":"Emergency this resource belongs to"},"emergencyName":{"type":"string","example":"Terremoto Venezuela 2026","nullable":true,"description":"Resolved emergency name, or null when it could not be found"},"author":{"nullable":true,"description":"Restricted contact of the real registrant when filed by an integration on their behalf (#235). Platform-admin only — never returned publicly.","type":"object","allOf":[{"$ref":"#/components/schemas/AuthorResponseDto"}]}},"required":["id","type","name","description","location","verificationLevel","publicStatus","ownerOrganizationId","accepts","contact","hasContact","schedule","manager","sourceName","externalUpdatedAt","country","city","capacity","occupancy","sourceOrganisation","sourceUrl","isFinalRecipient","recipientType","disputed","disputedAt","emergencyId","emergencyName"]},"PagedAdminResourcesDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ResourceAdminViewDto"}},"total":{"type":"number","example":123},"page":{"type":"number","example":1},"limit":{"type":"number","example":50}},"required":["items","total","page","limit"]},"ResourceAdminDetailDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"type":{"type":"string","enum":["collection_point","delivery_point","collection_and_delivery","warehouse","transport","supplier","venue"],"example":"collection_point"},"name":{"type":"string","example":"Cruz Roja Madrid"},"description":{"type":"string","example":"Centro de acopio principal","nullable":true},"location":{"$ref":"#/components/schemas/LocationViewDto"},"verificationLevel":{"type":"string","enum":["unverified","verified","official","rejected"],"example":"verified"},"publicStatus":{"type":"string","enum":["hidden","active","saturated","paused","closed","preparing","assisted"],"example":"active"},"ownerOrganizationId":{"type":"string","format":"uuid","nullable":true},"accepts":{"example":["water","food"],"type":"array","items":{"type":"string"}},"contact":{"type":"string","example":"+58 212 555 0000","nullable":true,"description":"Contact line. Personal data: nulled for anonymous callers on non-official resources — authenticate to reveal it. Use `hasContact` to tell \"hidden\" from \"absent\"."},"hasContact":{"type":"boolean","example":true,"description":"Whether a contact exists on file, regardless of whether `contact` is revealed to this caller."},"schedule":{"type":"string","example":"Lun-Vie 08-18","nullable":true},"manager":{"type":"string","example":"Juan Pérez","nullable":true},"sourceName":{"type":"string","example":"acopiove.org","nullable":true},"externalUpdatedAt":{"type":"string","example":"2026-06-27T00:00:00.000Z","description":"ISO 8601 date string","nullable":true},"country":{"type":"string","example":"Venezuela","description":"Country string as stored by the ingestion source (e.g. full Spanish name \"Venezuela\"). NOT guaranteed to be an ISO 3166-1 alpha-2 code — value depends on the source `pais` field.","nullable":true},"city":{"type":"string","example":"Caracas","nullable":true},"capacity":{"type":"number","example":200,"nullable":true,"description":"Total capacity of the place (e.g. shelter beds)"},"occupancy":{"type":"number","example":120,"nullable":true,"description":"Current occupancy of the place"},"sourceOrganisation":{"type":"string","example":"Protección Civil","nullable":true,"description":"Organisation that is the source of this record"},"sourceUrl":{"type":"string","example":"https://example.org/refugios/123","nullable":true,"description":"URL of the source record"},"isFinalRecipient":{"type":"boolean","example":false,"description":"Whether this resource is a final recipient of aid"},"recipientType":{"type":"string","example":"hospital","nullable":true,"description":"Recipient type slug (see the emergency recipient-type taxonomy)"},"disputed":{"type":"boolean","example":false,"description":"Whether enough citizens have reported this point as invalid; it stays visible with an \"in review\" warning until a coordinator resolves it."},"disputedAt":{"type":"string","example":"2026-06-28T12:00:00.000Z","nullable":true,"description":"When the point was flagged as disputed (ISO 8601), or null"},"emergencyId":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111","description":"Emergency this resource belongs to"},"emergencyName":{"type":"string","example":"Terremoto Venezuela 2026","nullable":true,"description":"Resolved emergency name, or null when it could not be found"},"author":{"nullable":true,"description":"Restricted contact of the real registrant when filed by an integration on their behalf (#235). Platform-admin only — never returned publicly.","type":"object","allOf":[{"$ref":"#/components/schemas/AuthorResponseDto"}]},"inventoryCategories":{"example":["water","hygiene"],"description":"Distinct categories of material this place has declared","type":"array","items":{"type":"string"}},"validityReports":{"description":"Citizen validity reports for this resource (open + resolved)","type":"array","items":{"$ref":"#/components/schemas/ValidityReportDto"}}},"required":["id","type","name","description","location","verificationLevel","publicStatus","ownerOrganizationId","accepts","contact","hasContact","schedule","manager","sourceName","externalUpdatedAt","country","city","capacity","occupancy","sourceOrganisation","sourceUrl","isFinalRecipient","recipientType","disputed","disputedAt","emergencyId","emergencyName","inventoryCategories","validityReports"]},"RecipientTypeDto":{"type":"object","properties":{"slug":{"type":"string","example":"hospital"},"labelEs":{"type":"string","example":"Hospital"},"labelEn":{"type":"string","example":"Hospital"},"sort":{"type":"number","example":10}},"required":["slug","labelEs","labelEn","sort"]},"CreateTemplateDto":{"type":"object","properties":{"name":{"type":"string","example":"Terremoto básico","minLength":2},"description":{"type":"string","example":"Template para emergencias sísmicas de nivel moderado"},"dontBringList":{"example":["mascotas","joyas","vehículos grandes"],"description":"Items that volunteers should NOT bring","type":"array","items":{"type":"string"}},"defaultAnnouncement":{"type":"string","example":"No se aceptan mascotas en el centro de acopio.","nullable":true}},"required":["name","description","dontBringList"]},"CreateTemplateResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"}},"required":["id"]},"TemplateViewDto":{"type":"object","properties":{"id":{"type":"string","example":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"},"name":{"type":"string","example":"Terremoto básico"},"description":{"type":"string","example":"Template para emergencias sísmicas"},"dontBringList":{"example":["mascotas","joyas"],"type":"array","items":{"type":"string"}},"defaultAnnouncement":{"type":"string","example":"No se aceptan mascotas.","nullable":true},"createdAt":{"type":"string","example":"2026-06-27T10:00:00.000Z"}},"required":["id","name","description","dontBringList","defaultAnnouncement","createdAt"]},"CreateEmergencyDto":{"type":"object","properties":{"name":{"type":"string","example":"Emergencia sísmica — Venezuela","minLength":2},"slug":{"type":"string","example":"venezuela","description":"URL-safe slug (lowercase letters, digits, hyphens). Auto-generated from name if omitted.","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"country":{"type":"string","example":"VE","minLength":2,"description":"ISO 3166-1 alpha-2 country code"}},"required":["name","country"]},"CreateEmergencyResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"11111111-1111-4111-8111-111111111111"},"slug":{"type":"string","example":"venezuela"}},"required":["id","slug"]},"EmergencyViewDto":{"type":"object","properties":{"id":{"type":"string","example":"11111111-1111-4111-8111-111111111111"},"name":{"type":"string","example":"Emergencia sísmica — Venezuela"},"slug":{"type":"string","example":"venezuela"},"country":{"type":"string","example":"VE"},"status":{"type":"string","example":"active","enum":["active","paused","closed"]},"announcement":{"type":"string","example":"El puente de acceso norte está cortado.","nullable":true},"dontBringList":{"example":["mascotas","joyas","vehículos grandes"],"description":"Items volunteers should NOT bring to the emergency","type":"array","items":{"type":"string"}},"updatedAt":{"type":"string","example":"2026-06-25T10:00:00.000Z"}},"required":["id","name","slug","country","status","announcement","dontBringList","updatedAt"]},"MyEmergencyViewDto":{"type":"object","properties":{"id":{"type":"string","example":"11111111-1111-4111-8111-111111111111"},"name":{"type":"string","example":"Emergencia sísmica — Venezuela"},"slug":{"type":"string","example":"venezuela"},"country":{"type":"string","example":"VE"},"status":{"type":"string","example":"active","enum":["active","paused","closed"]},"announcement":{"type":"string","example":"El puente de acceso norte está cortado.","nullable":true},"dontBringList":{"example":["mascotas","joyas","vehículos grandes"],"description":"Items volunteers should NOT bring to the emergency","type":"array","items":{"type":"string"}},"updatedAt":{"type":"string","example":"2026-06-25T10:00:00.000Z"},"roleIds":{"example":["emergency_verifier","emergency_coordinator"],"description":"Role ids the principal holds at this emergency scope","type":"array","items":{"type":"string"}},"resourceDisputeThreshold":{"type":"number","example":5,"nullable":true,"description":"Umbral de disputa configurado para esta emergencia, o null cuando usa el global. Solo se expone en la vista autenticada."}},"required":["id","name","slug","country","status","announcement","dontBringList","updatedAt","roleIds","resourceDisputeThreshold"]},"CreateEmergencyFromTemplateDto":{"type":"object","properties":{"templateId":{"type":"string","example":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"},"name":{"type":"string","example":"Terremoto Valencia 2026","minLength":2},"slug":{"type":"string","example":"terremoto-valencia-2026","description":"URL-safe slug (lowercase letters, digits, hyphens)","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"country":{"type":"string","example":"ES","minLength":2}},"required":["templateId","name","slug","country"]},"PublishAnnouncementDto":{"type":"object","properties":{"message":{"type":"string","example":"Se suspenden las operaciones de rescate hasta nuevo aviso."}},"required":["message"]},"SetDisputeThresholdDto":{"type":"object","properties":{"threshold":{"type":"number","example":5,"nullable":true,"minimum":1,"maximum":1000,"description":"Número mínimo de reportantes distintos para marcar el punto como disputado (entero entre 1 y 1000). Enviar null explícito elimina el umbral específico y vuelve al global (RESOURCE_DISPUTE_THRESHOLD / 3). El campo es obligatorio: un body vacío es 400."}},"required":["threshold"]},"NeedLocationDto":{"type":"object","properties":{"address":{"type":"string","example":"123 Main Street, Caracas, Venezuela"},"latitude":{"type":"number","example":10.4806,"description":"Latitude (-90 to 90)"},"longitude":{"type":"number","example":-66.9036,"description":"Longitude (-180 to 180)"}},"required":["address","latitude","longitude"]},"CreateNeedDto":{"type":"object","properties":{"title":{"type":"string","example":"Alimentos para 50 familias","minLength":2},"description":{"type":"string","example":"Descripción detallada de la necesidad"},"location":{"$ref":"#/components/schemas/NeedLocationDto"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"example":"high"},"requesterOrganizationId":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6","description":"Organization on whose behalf the request is made (optional)"},"items":{"description":"List of items needed (minimum 1)","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"requiredSkill":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"],"example":"medical","description":"Required volunteer skill for personnel needs","nullable":true},"skillSpecialty":{"type":"string","example":"Médico urgencias pediátricas","description":"Free-text specialty detail (coordinator-only, not exposed publicly)","nullable":true},"requestedCount":{"type":"number","example":3,"description":"How many personnel are needed (>= 1)","nullable":true},"resourceId":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6","description":"Link this need to a resource / final recipient (#60). Optional.","nullable":true},"author":{"description":"Restricted contact of the real person this need is filed on behalf of (#235). Optional; only meaningful — and required — when a trusted integration writes via API key. Never exposed on public reads.","allOf":[{"$ref":"#/components/schemas/AuthorDto"}]}},"required":["title","location","priority","items"]},"CreateNeedResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"required":["id"]},"NeedLocationResponseDto":{"type":"object","properties":{"address":{"type":"string","example":"Caracas","nullable":true,"description":"Street line is coarsened (locality only) or dropped for needs with approximate location sensitivity to protect the requester’s privacy."},"latitude":{"type":"number","example":10.4806},"longitude":{"type":"number","example":-66.9036}},"required":["latitude","longitude"]},"NeedViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"emergencyId":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"title":{"type":"string","example":"Alimentos para 50 familias"},"description":{"type":"string","example":"Descripción detallada","nullable":true},"location":{"$ref":"#/components/schemas/NeedLocationResponseDto"},"locationSensitivity":{"type":"string","enum":["public","approximate"],"example":"approximate","description":"When \"approximate\", the coordinates in location are jittered for privacy. Coordinators always receive exact coordinates regardless of this value."},"priority":{"type":"string","enum":["low","medium","high","urgent"],"example":"high"},"requesterOrganizationId":{"type":"string","format":"uuid","nullable":true},"managingOrganizationId":{"type":"string","format":"uuid","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}},"status":{"type":"string","enum":["pending","validated","rejected","fulfilled"],"example":"pending"},"createdAt":{"type":"string","example":"2024-01-01T00:00:00.000Z"},"expiresAt":{"type":"string","example":"2024-01-03T00:00:00.000Z","nullable":true,"description":"Timestamp when the need expires (48 h after validation). Null for legacy needs."},"lastVerifiedAt":{"type":"string","example":"2024-01-01T12:00:00.000Z","nullable":true,"description":"Timestamp when the need was last verified by a coordinator."},"requiredSkill":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"],"nullable":true,"description":"Required volunteer skill (personnel needs only)"},"requestedCount":{"type":"number","example":3,"nullable":true,"description":"Number of personnel needed"},"resourceId":{"type":"string","format":"uuid","nullable":true,"description":"Linked resource / final recipient id (#60), or null if standalone."}},"required":["id","emergencyId","title","location","locationSensitivity","priority","items","status","createdAt"]},"NearbyNeedViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"emergencyId":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"title":{"type":"string","example":"Alimentos para 50 familias"},"description":{"type":"string","example":"Descripción detallada","nullable":true},"location":{"$ref":"#/components/schemas/NeedLocationResponseDto"},"locationSensitivity":{"type":"string","enum":["public","approximate"],"example":"approximate","description":"When \"approximate\", the coordinates in location are jittered for privacy. Coordinators always receive exact coordinates regardless of this value."},"priority":{"type":"string","enum":["low","medium","high","urgent"],"example":"high"},"requesterOrganizationId":{"type":"string","format":"uuid","nullable":true},"managingOrganizationId":{"type":"string","format":"uuid","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}},"status":{"type":"string","enum":["pending","validated","rejected","fulfilled"],"example":"pending"},"createdAt":{"type":"string","example":"2024-01-01T00:00:00.000Z"},"expiresAt":{"type":"string","example":"2024-01-03T00:00:00.000Z","nullable":true,"description":"Timestamp when the need expires (48 h after validation). Null for legacy needs."},"lastVerifiedAt":{"type":"string","example":"2024-01-01T12:00:00.000Z","nullable":true,"description":"Timestamp when the need was last verified by a coordinator."},"requiredSkill":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"],"nullable":true,"description":"Required volunteer skill (personnel needs only)"},"requestedCount":{"type":"number","example":3,"nullable":true,"description":"Number of personnel needed"},"resourceId":{"type":"string","format":"uuid","nullable":true,"description":"Linked resource / final recipient id (#60), or null if standalone."},"distanceMeters":{"type":"number","example":1850,"description":"Distance in meters from the queried location to the (public) need location."}},"required":["id","emergencyId","title","location","locationSensitivity","priority","items","status","createdAt","distanceMeters"]},"NearbyNeedsResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/NearbyNeedViewDto"}}},"required":["items"]},"InBoundsNeedsDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/NeedViewDto"}}},"required":["items"]},"EditNeedDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo de la edición (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Se corrige la prioridad y se completa la descripción"},"title":{"type":"string","description":"Nuevo título (omitir para no cambiarlo)","minLength":2},"description":{"type":"string","description":"Nueva descripción. Cadena vacía la borra. Omitir para no cambiarla.","nullable":true},"priority":{"type":"string","enum":["low","medium","high","urgent"],"description":"Nueva prioridad"}},"required":["reason"]},"DiscardNeedDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo del descarte (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Petición duplicada; ya validada en otra entrada"}},"required":["reason"]},"AssignNeedManagerDto":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6","description":"ID of the organization that will manage this need"}},"required":["organizationId"]},"VolunteerSuggestionDto":{"type":"object","properties":{"volunteerId":{"type":"string","format":"uuid","description":"Volunteer record ID"},"userId":{"type":"string","format":"uuid","description":"User account ID"},"name":{"type":"string","example":"Ana García"},"skills":{"description":"All volunteer skills","type":"array","items":{"type":"string"}},"hasVehicle":{"type":"boolean","description":"Whether the volunteer has any vehicle"},"availability":{"type":"string","description":"Volunteer availability level"}},"required":["volunteerId","userId","name","skills","hasVehicle","availability"]},"CreateTaskFromNeedDto":{"type":"object","properties":{"volunteerIds":{"description":"IDs of volunteers to assign to the task (optional)","type":"array","items":{"type":"string","format":"uuid"}},"dueDate":{"type":"string","example":"2025-12-31","description":"ISO date string for task due date (optional, informational)","nullable":true}}},"CreatedTaskFromNeedDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string"},"requiredSkill":{"type":"string","nullable":true,"enum":["driving","medical","logistics","cooking","languages","admin","general"]},"linkedNeedId":{"type":"string","nullable":true,"format":"uuid"},"status":{"type":"string"},"createdByUserId":{"type":"string","format":"uuid"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"assignments":{"type":"array","items":{"type":"object","properties":{"volunteerId":{"type":"string","format":"uuid"},"assignedAt":{"type":"string","format":"date-time"},"checkedInAt":{"type":"string","format":"date-time","nullable":true},"checkedOutAt":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string"}}}}},"required":["id","emergencyId","title","description","status","createdByUserId","createdAt","updatedAt","assignments"]},"CreateOrganizationDto":{"type":"object","properties":{"name":{"type":"string","example":"Red Cross Spain"},"type":{"type":"string","enum":["ngo","company","public_admin","association","transport_operator","other"],"example":"ngo"},"taxId":{"type":"string","example":"ES-12345678"},"contactEmail":{"type":"string","example":"contact@org.example"},"contactPhone":{"type":"string","example":"+58 412-1234567"}},"required":["name","type"]},"CreateOrganizationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"New organization UUID"}},"required":["id"]},"OrganizationViewDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["ngo","company","public_admin","association","transport_operator","other"]},"verificationLevel":{"type":"string"}},"required":["id","name","type","verificationLevel"]},"OrganizationAdminListItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["ngo","company","public_admin","association","transport_operator","other"]},"taxId":{"type":"object","nullable":true,"example":"ES-12345678"},"contactEmail":{"type":"object","nullable":true,"example":"contact@org.example"},"contactPhone":{"type":"object","nullable":true,"example":"+58 412-1234567"},"verificationLevel":{"type":"string","example":"unverified"},"memberCount":{"type":"number","example":3},"accreditationStatus":{"type":"string","enum":["global","emergency","none"],"example":"none"}},"required":["id","name","type","taxId","contactEmail","contactPhone","verificationLevel","memberCount","accreditationStatus"]},"OrganizationMemberDto":{"type":"object","properties":{"userId":{"type":"string","description":"User UUID"},"email":{"type":"string","example":"member@example.com"},"name":{"type":"string","example":"Jane Doe"},"role":{"type":"string","enum":["owner","member"],"example":"member"}},"required":["userId","email","name","role"]},"OrganizationServiceAccountDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"createdAt":{"type":"string","description":"ISO 8601 timestamp"},"keyCount":{"type":"number","example":2},"activeKeyCount":{"type":"number","example":1}},"required":["id","name","createdAt","keyCount","activeKeyCount"]},"OrganizationAccreditationDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"scope":{"type":"object","description":"\"global\" or an object with the emergency UUID","example":"global"},"grantedByUserId":{"type":"string","format":"uuid"},"grantedAt":{"type":"string","description":"ISO 8601 timestamp"},"evidence":{"type":"object","nullable":true}},"required":["id","scope","grantedByUserId","grantedAt","evidence"]},"OrganizationAdminDetailDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["ngo","company","public_admin","association","transport_operator","other"]},"taxId":{"type":"object","nullable":true},"contactEmail":{"type":"object","nullable":true},"contactPhone":{"type":"object","nullable":true},"verificationLevel":{"type":"string","example":"unverified"},"createdAt":{"type":"string","description":"ISO 8601 timestamp"},"accreditationStatus":{"type":"string","enum":["global","emergency","none"],"example":"none"},"members":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationMemberDto"}},"serviceAccounts":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationServiceAccountDto"}},"accreditations":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationAccreditationDto"}},"emergencyIds":{"description":"UUIDs of emergencies the organization participates in","type":"array","items":{"type":"string"}}},"required":["id","name","type","taxId","contactEmail","contactPhone","verificationLevel","createdAt","accreditationStatus","members","serviceAccounts","accreditations","emergencyIds"]},"AddMemberDto":{"type":"object","properties":{"email":{"type":"string","example":"member@example.com"}},"required":["email"]},"GrantAccreditationDto":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid","description":"Organization to accredit"},"scope":{"description":"Scope: \"global\" or { emergencyId: \"<uuid>\" }","oneOf":[{"type":"string","enum":["global"]},{"type":"object","properties":{"emergencyId":{"type":"string","format":"uuid"}},"required":["emergencyId"]}]},"evidence":{"type":"string","description":"Free-text evidence / justification"}},"required":["organizationId","scope"]},"AccreditationResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]},"AccreditationViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"scope":{"type":"object"},"grantedByUserId":{"type":"string","format":"uuid"},"grantedAt":{"type":"string","description":"ISO 8601 timestamp"},"evidence":{"type":"object","nullable":true}},"required":["id","organizationId","scope","grantedByUserId","grantedAt"]},"GeocodeResultDto":{"type":"object","properties":{"address":{"type":"string","example":"Madrid, Community of Madrid, Spain"},"latitude":{"type":"number","example":40.4165,"description":"Decimal degrees latitude"},"longitude":{"type":"number","example":-3.70256,"description":"Decimal degrees longitude"}},"required":["address","latitude","longitude"]},"NeedsMetricsDto":{"type":"object","properties":{"total":{"type":"number","description":"Total needs registered for this emergency","example":10},"open":{"type":"number","description":"Open needs: pending + validated","example":7},"closed":{"type":"number","description":"Closed needs: fulfilled","example":2}},"required":["total","open","closed"]},"ResourcesMetricsDto":{"type":"object","properties":{"total":{"type":"number","description":"Total resources registered for this emergency","example":5},"active":{"type":"number","description":"Active logistic points (publicStatus = Active)","example":3},"pending":{"type":"number","description":"Resources pending publication (publicStatus = Hidden)","example":2}},"required":["total","active","pending"]},"EmergencyMetricsDto":{"type":"object","properties":{"needs":{"$ref":"#/components/schemas/NeedsMetricsDto"},"resources":{"$ref":"#/components/schemas/ResourcesMetricsDto"}},"required":["needs","resources"]},"OfferLocationDto":{"type":"object","properties":{"address":{"type":"string","example":"123 Main Street, Caracas, Venezuela"},"latitude":{"type":"number","example":10.4806,"description":"Latitude (-90 to 90)"},"longitude":{"type":"number","example":-66.9036,"description":"Longitude (-180 to 180)"}},"required":["address","latitude","longitude"]},"SubmitOfferDto":{"type":"object","properties":{"items":{"description":"Supply lines offered (at least one)","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"location":{"$ref":"#/components/schemas/OfferLocationDto"},"targetNeedId":{"type":"string","format":"uuid","description":"Target need this offer is directed at (optional)"},"donorOrganizationId":{"type":"string","format":"uuid","description":"Organization on whose behalf the offer is made (optional)"},"notes":{"type":"string","example":"Available for pickup Mon-Fri","description":"Additional notes (optional)"},"author":{"description":"Restricted contact of the real donor this offer is filed on behalf of (#235). Optional; required when a trusted integration writes via API key. Never exposed on public reads.","allOf":[{"$ref":"#/components/schemas/AuthorDto"}]}},"required":["items","location"]},"SubmitOfferResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"required":["id"]},"OfferLocationResponseDto":{"type":"object","properties":{"address":{"type":"string","example":"123 Main Street, Caracas"},"latitude":{"type":"number","example":10.4806},"longitude":{"type":"number","example":-66.9036}},"required":["address","latitude","longitude"]},"OfferViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"donorUserId":{"type":"string","format":"uuid"},"donorOrganizationId":{"type":"string","format":"uuid","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}},"location":{"$ref":"#/components/schemas/OfferLocationResponseDto"},"targetNeedId":{"type":"string","format":"uuid","nullable":true},"matchedNeedId":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["open","matched","fulfilled","cancelled"],"example":"open"},"notes":{"type":"string","example":"Available Mon-Fri","nullable":true},"createdAt":{"type":"string","example":"2024-01-01T00:00:00.000Z"},"updatedAt":{"type":"string","example":"2024-01-01T00:00:00.000Z"},"author":{"nullable":true,"description":"Restricted contact of the real donor when filed by an integration on their behalf (#235). Coordinator/owner-only — never returned publicly.","type":"object","allOf":[{"$ref":"#/components/schemas/AuthorResponseDto"}]}},"required":["id","emergencyId","donorUserId","items","location","status","createdAt","updatedAt"]},"MatchOfferDto":{"type":"object","properties":{"needId":{"type":"string","format":"uuid","description":"The need to match this offer against"}},"required":["needId"]},"EditOfferDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo de la edición (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Se corrigen las líneas de la oferta"},"items":{"description":"Nuevas líneas de la oferta (reemplazan la lista). Omitir para no cambiarlas.","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"notes":{"type":"string","description":"Nuevas notas. Cadena vacía las borra. Omitir para no cambiarlas.","nullable":true}},"required":["reason"]},"DiscardOfferDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo del descarte (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Oferta duplicada; ya gestionada en otra entrada"}},"required":["reason"]},"CreateDonationIntakeDto":{"type":"object","properties":{"targetResourceId":{"type":"string","format":"uuid","description":"Target collection point"},"donorName":{"type":"string","example":"María López"},"donorPhone":{"type":"string","example":"+52 55 1234 5678","nullable":true},"donorEmail":{"type":"string","example":"maria@example.com","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}}},"required":["targetResourceId","donorName","items"]},"CreateDonationIntakeResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"intakeCode":{"type":"string","example":"ACO-7F3K"},"status":{"type":"string","enum":["pending","received","rejected","incomplete"],"example":"pending"}},"required":["id","intakeCode","status"]},"LookupDonorByContactDto":{"type":"object","properties":{"donorPhone":{"type":"string","example":"+52 55 1234 5678","nullable":true},"donorEmail":{"type":"string","example":"maria@example.com","nullable":true}}},"PendingIntakeSummaryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"itemCount":{"type":"number","example":2},"createdAt":{"type":"string","format":"date-time"}},"required":["id","itemCount","createdAt"]},"LookupDonorByContactResponseDto":{"type":"object","properties":{"donorName":{"type":"string","example":"María López","nullable":true},"pendingIntakes":{"type":"array","items":{"$ref":"#/components/schemas/PendingIntakeSummaryDto"}}},"required":["pendingIntakes"]},"DonationIntakeTrackingDto":{"type":"object","properties":{"intakeCode":{"type":"string","example":"ACO-7F3K"},"status":{"type":"string","enum":["pending","received","rejected","incomplete"]},"resourceName":{"type":"string","example":"Acopio CDMX Norte","nullable":true},"createdAt":{"type":"string","format":"date-time"},"receivedAt":{"type":"string","format":"date-time","nullable":true},"updatedAt":{"type":"string","format":"date-time"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}}},"required":["intakeCode","status","createdAt","updatedAt","lines"]},"IncomingSummaryLineDto":{"type":"object","properties":{"name":{"type":"string","example":"Agua embotellada"},"category":{"type":"string","example":"water"},"unit":{"type":"string","nullable":true},"presentation":{"type":"string","nullable":true},"totalQuantity":{"type":"number","description":"Total expected across all pending intakes for this line","example":200},"intakeCount":{"type":"number","description":"Distinct pending intakes contributing to this line","example":3}},"required":["name","category","totalQuantity","intakeCount"]},"IncomingSummaryDto":{"type":"object","properties":{"lines":{"type":"array","items":{"$ref":"#/components/schemas/IncomingSummaryLineDto"}},"totalPendingIntakes":{"type":"number","example":5}},"required":["lines","totalPendingIntakes"]},"MyDonationIntakeDto":{"type":"object","properties":{"intakeCode":{"type":"string","example":"ACO-7F3K"},"status":{"type":"string","enum":["pending","received","rejected","incomplete"]},"emergencyId":{"type":"string","format":"uuid"},"emergencySlug":{"type":"string","example":"terremoto-venezuela-2026","nullable":true},"resourceName":{"type":"string","example":"Acopio CDMX Norte","nullable":true},"itemCount":{"type":"number","example":3},"createdAt":{"type":"string","format":"date-time"},"receivedAt":{"type":"string","format":"date-time","nullable":true}},"required":["intakeCode","status","emergencyId","itemCount","createdAt"]},"UpdateDonationIntakeDto":{"type":"object","properties":{"intakeCode":{"type":"string","example":"ACO-7F3K"},"donorName":{"type":"string","example":"María López"},"donorPhone":{"type":"string","example":"+52 55 1234 5678","nullable":true},"donorEmail":{"type":"string","example":"maria@example.com","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}}},"required":["intakeCode","donorName","items"]},"IntakeLineViewDto":{"type":"object","properties":{"name":{"type":"string","example":"Water bottles"},"supplyId":{"type":"string","example":"1e4b5f3b-5c9c-4f77-8f50-3d2dbdc0c7d8","nullable":true,"description":"Optional soft link to the canonical supply master data."},"quantity":{"type":"number","example":100},"unit":{"type":"string","example":"liters","nullable":true},"category":{"type":"string","example":"water","description":"Slug de categoría de material (data-driven, lowercase snake_case)."},"presentation":{"type":"string","example":"ampolla","nullable":true,"description":"Presentation / route of administration (ampolla, EV, inhalador…) — #61."},"expiresAt":{"type":"string","example":"2026-07-01","nullable":true,"format":"date","description":"Optional freshness date for the line, expressed as an ISO date (YYYY-MM-DD)."},"id":{"type":"string","format":"uuid"},"sortOrder":{"type":"number"}},"required":["name","supplyId","quantity","category","id","sortOrder"]},"PublicDonationIntakeDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"targetResourceId":{"type":"string","format":"uuid"},"intakeCode":{"type":"string","example":"ACO-7F3K"},"status":{"type":"string","enum":["pending","received","rejected","incomplete"]},"donorName":{"type":"string"},"donorPhone":{"type":"string","nullable":true},"donorEmail":{"type":"string","nullable":true},"lines":{"type":"array","items":{"$ref":"#/components/schemas/IntakeLineViewDto"}},"receivedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","emergencyId","targetResourceId","intakeCode","status","donorName","lines","createdAt","updatedAt"]},"DonationIntakeSearchHitDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"intakeCode":{"type":"string","example":"ACO-7F3K"},"donorName":{"type":"string"},"donorPhone":{"type":"string","nullable":true},"donorEmail":{"type":"string","nullable":true},"status":{"type":"string","enum":["pending","received","rejected","incomplete"]},"targetResourceId":{"type":"string","format":"uuid"},"itemCount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","intakeCode","donorName","status","targetResourceId","itemCount","createdAt"]},"DonationIntakeViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"targetResourceId":{"type":"string","format":"uuid"},"intakeCode":{"type":"string","example":"ACO-7F3K"},"status":{"type":"string","enum":["pending","received","rejected","incomplete"]},"donorName":{"type":"string"},"donorPhone":{"type":"string","nullable":true},"donorEmail":{"type":"string","nullable":true},"donorUserId":{"type":"string","format":"uuid","nullable":true},"lines":{"type":"array","items":{"$ref":"#/components/schemas/IntakeLineViewDto"}},"volunteerNotes":{"type":"string","nullable":true},"evidenceFileKey":{"type":"string","nullable":true},"receptionAdjustmentReason":{"type":"string","nullable":true,"description":"Reason recorded when received lines differed from declared"},"receivedAt":{"type":"string","format":"date-time","nullable":true},"receivedByUserId":{"type":"string","format":"uuid","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","emergencyId","targetResourceId","intakeCode","status","donorName","lines","createdAt","updatedAt"]},"IntakeDeepLinkDto":{"type":"object","properties":{"url":{"type":"string","example":"http://localhost:3001/e/mexico-demo/pre-registro?resourceId=33333333-3333-4333-8333-333333333331"},"resourceName":{"type":"string","example":"Acopio CDMX Norte"},"slug":{"type":"string","example":"mexico-demo"},"resourceId":{"type":"string","format":"uuid"}},"required":["url","resourceName","slug","resourceId"]},"ReceiveDonationIntakeDto":{"type":"object","properties":{"volunteerNotes":{"type":"string","nullable":true},"evidenceFileKey":{"type":"string","description":"File key from POST /files","nullable":true},"items":{"description":"Lines actually received; when omitted the declared lines stand (#129)","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"adjustmentReason":{"type":"string","description":"Required when received lines differ from the declared ones","nullable":true}}},"RejectDonationIntakeDto":{"type":"object","properties":{"volunteerNotes":{"type":"string","nullable":true}}},"MarkDonationIntakeIncompleteDto":{"type":"object","properties":{"volunteerNotes":{"type":"string","nullable":true}}},"PublishCapacityProviderDto":{"type":"object","properties":{"type":{"type":"string","enum":["volunteer","organization"],"example":"volunteer"},"id":{"type":"string","format":"uuid","description":"Volunteer or organization id (polymorphic, no FK)"}},"required":["type","id"]},"CapacityAmountDto":{"type":"object","properties":{"weightKg":{"type":"number","example":1500,"description":"Carrying weight in kilograms (positive)","nullable":true},"volumeM3":{"type":"number","example":12,"description":"Carrying volume in cubic metres (positive)","nullable":true}}},"CoverageDto":{"type":"object","properties":{"kind":{"type":"string","enum":["corridor","area"],"example":"corridor"},"originResourceId":{"type":"string","format":"uuid","description":"Corridor: origin collection point (resource) id"},"destinationResourceId":{"type":"string","format":"uuid","description":"Corridor: destination collection point (resource) id"},"originLat":{"type":"number","example":10.4806,"description":"Corridor: origin lat"},"originLng":{"type":"number","example":-66.9036,"description":"Corridor: origin lng"},"destinationLat":{"type":"number","example":10.6,"description":"Corridor: destination lat"},"destinationLng":{"type":"number","example":-67,"description":"Corridor: destination lng"},"area":{"type":"string","example":"Estado Vargas","description":"Area: free-text served area (required when kind=area)"}},"required":["kind"]},"CapacityWindowDto":{"type":"object","properties":{"from":{"type":"string","example":"2026-07-01T00:00:00.000Z","description":"Availability start (ISO-8601)"},"to":{"type":"string","example":"2026-07-31T00:00:00.000Z","description":"Availability end (ISO-8601)"}}},"PublishCapacityDto":{"type":"object","properties":{"emergencyId":{"type":"string","format":"uuid","description":"Emergency this capacity serves"},"provider":{"$ref":"#/components/schemas/PublishCapacityProviderDto"},"mode":{"type":"string","enum":["road","sea","air"],"example":"road"},"capacity":{"description":"At least one of weightKg / volumeM3 is required","allOf":[{"$ref":"#/components/schemas/CapacityAmountDto"}]},"coverage":{"$ref":"#/components/schemas/CoverageDto"},"window":{"$ref":"#/components/schemas/CapacityWindowDto"},"constraints":{"example":["refrigerated","hazmat"],"description":"Free-form constraints","type":"array","items":{"type":"string"}},"notes":{"type":"string","example":"Salida diaria a las 08:00","description":"Additional notes"}},"required":["emergencyId","provider","mode","capacity","coverage"]},"PublishCapacityResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"required":["id"]},"CapacityAmountResponseDto":{"type":"object","properties":{"weightKg":{"type":"number","example":1500,"nullable":true},"volumeM3":{"type":"number","example":12,"nullable":true}}},"CoverageResponseDto":{"type":"object","properties":{"kind":{"type":"string","enum":["corridor","area"],"example":"corridor"},"originResourceId":{"type":"string","format":"uuid","nullable":true},"destinationResourceId":{"type":"string","format":"uuid","nullable":true},"originLat":{"type":"number","example":10.4806,"nullable":true},"originLng":{"type":"number","example":-66.9036,"nullable":true},"destinationLat":{"type":"number","example":10.6,"nullable":true},"destinationLng":{"type":"number","example":-67,"nullable":true},"area":{"type":"string","example":"Estado Vargas"}},"required":["kind"]},"CapacityWindowResponseDto":{"type":"object","properties":{"from":{"type":"string","example":"2026-07-01T00:00:00.000Z","nullable":true},"to":{"type":"string","example":"2026-07-31T00:00:00.000Z","nullable":true}}},"CapacityViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"providerType":{"type":"string","enum":["volunteer","organization"],"example":"volunteer"},"providerId":{"type":"string","format":"uuid"},"mode":{"type":"string","enum":["road","sea","air"],"example":"road"},"capacity":{"$ref":"#/components/schemas/CapacityAmountResponseDto"},"coverage":{"$ref":"#/components/schemas/CoverageResponseDto"},"window":{"$ref":"#/components/schemas/CapacityWindowResponseDto"},"constraints":{"example":["refrigerated"],"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["available","reserved","withdrawn"],"example":"available"},"notes":{"type":"string","example":"Salida diaria a las 08:00","nullable":true},"createdAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"},"updatedAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"}},"required":["id","emergencyId","providerType","providerId","mode","capacity","coverage","window","constraints","status","createdAt","updatedAt"]},"CreateShipmentDto":{"type":"object","properties":{"emergencyId":{"type":"string","format":"uuid","description":"Emergency this shipment serves"},"originResourceId":{"type":"string","format":"uuid","description":"Origin resource node (collection point) id"},"destinationResourceId":{"type":"string","format":"uuid","description":"Destination resource node id"},"items":{"description":"Loose cargo lines (canonical SupplyLine). Optional when containers are loaded — a shipment must carry at least lines or containers.","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"containerIds":{"description":"Trackable containers (#140) loaded onto the expedition. Optional when loose lines are provided.","type":"array","items":{"type":"string","format":"uuid"}},"hubId":{"type":"string","format":"uuid","description":"Optional logistics hub (#150) this expedition transits. A hub_manager grant scoped to it may operate the shipment cross-emergency (§16.3). No FK."},"manifest":{"type":"string","example":"Carga frágil, manipular con cuidado","description":"Free-text cargo manifest note"}},"required":["emergencyId","originResourceId","destinationResourceId"]},"CreateShipmentResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"required":["id"]},"AssignShipmentCarrierDto":{"type":"object","properties":{"type":{"type":"string","enum":["volunteer","organization"],"example":"volunteer"},"id":{"type":"string","format":"uuid","description":"Volunteer or organization id (polymorphic, no FK)"}},"required":["type","id"]},"AssignCapacityToShipmentDto":{"type":"object","properties":{"assignedCapacityId":{"type":"string","format":"uuid","description":"TransportCapacity (#105) to earmark for this shipment"},"carrier":{"description":"Optional carrier. Omit for an internal inventory transfer (no carrier).","allOf":[{"$ref":"#/components/schemas/AssignShipmentCarrierDto"}]}},"required":["assignedCapacityId"]},"ShipmentViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","description":"Legible/QR \"Código Único\" of the expedition (#163)","example":"EXP-0001"},"emergencyId":{"type":"string","format":"uuid"},"originResourceId":{"type":"string","format":"uuid"},"destinationResourceId":{"type":"string","format":"uuid"},"items":{"description":"Loose cargo lines (canonical SupplyLine, #141)","type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}},"containerIds":{"description":"Trackable containers (#140) loaded onto the expedition","type":"array","items":{"type":"string","format":"uuid"}},"assignedCapacityId":{"type":"string","format":"uuid","nullable":true},"carrierType":{"type":"string","enum":["volunteer","organization"],"nullable":true,"example":"volunteer"},"carrierId":{"type":"string","format":"uuid","nullable":true},"hubId":{"type":"string","format":"uuid","nullable":true,"description":"Logistics hub (#150) this expedition transits, or null. Grants scoped to it confer cross-emergency authority over the shipment (§16.3)."},"manifest":{"type":"string","example":"Carga frágil","nullable":true},"status":{"type":"string","enum":["planned","assigned","in_transit","delivered","failed","cancelled"],"example":"planned"},"createdAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"},"updatedAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"}},"required":["id","code","emergencyId","originResourceId","destinationResourceId","items","containerIds","status","createdAt","updatedAt"]},"CategoryDto":{"type":"object","properties":{"slug":{"type":"string","example":"medicines","description":"Canonical category slug (stable identifier)"},"label":{"type":"string","example":"Medicamentos","description":"Etiqueta resuelta en el locale pedido (fallback a `es`)"},"parentSlug":{"type":"string","example":"medical","nullable":true,"description":"Parent category slug, or null for a top-level category"},"vertical":{"type":"string","example":"general"},"sort":{"type":"number","example":41,"description":"Display sort order"},"codePrefix":{"type":"string","example":"MED","nullable":true,"description":"Unique 3-letter prefix for supplies in this category, or null if inherited"},"kind":{"type":"string","enum":["material","personnel"],"example":"material","description":"Whether the category is aid material or personnel. Personnel (medical_personnel) is excluded from material pickers."}},"required":["slug","label","parentSlug","vertical","sort","codePrefix","kind"]},"CategoryTranslationDto":{"type":"object","properties":{"locale":{"type":"string","example":"fr"},"label":{"type":"string","example":"Nourriture"}},"required":["locale","label"]},"CategoryAdminDto":{"type":"object","properties":{"slug":{"type":"string","example":"baby_food"},"label":{"type":"string","example":"Alimentos para bebé","description":"Localized label"},"labelEs":{"type":"string","example":"Alimentos para bebé"},"labelEn":{"type":"string","example":"Baby food"},"parentSlug":{"type":"string","example":"food","nullable":true,"description":"Parent category slug, or null for a top-level category"},"vertical":{"type":"string","example":"general"},"sort":{"type":"number","example":140},"archivedAt":{"type":"object","example":null,"nullable":true,"description":"Soft-archive timestamp"},"translations":{"type":"array","items":{"$ref":"#/components/schemas/CategoryTranslationDto"}},"externalCodes":{"type":"object","additionalProperties":{"type":"string"},"description":"Códigos externos estándar para interop (#398): mapa namespace→código. `{}` si no tiene.","example":{"unspsc":"51101500"}}},"required":["slug","label","labelEs","labelEn","parentSlug","vertical","sort","archivedAt","translations","externalCodes"]},"CreateCategoryDto":{"type":"object","properties":{"slug":{"type":"string","example":"baby_food"},"labelEs":{"type":"string","example":"Alimentos para bebé"},"labelEn":{"type":"string","example":"Baby food"},"parentSlug":{"type":"object","example":"food","nullable":true,"description":"Parent category slug, or null for a top-level category"},"vertical":{"type":"string","example":"general"},"sort":{"type":"number","example":140},"translations":{"description":"Additional locale labels to persist in category_translations","type":"array","items":{"$ref":"#/components/schemas/CategoryTranslationDto"}},"externalCodes":{"type":"object","additionalProperties":{"type":"string"},"description":"Códigos externos estándar para interop (#398): mapa namespace→código."}},"required":["slug","labelEs","labelEn","vertical","sort"]},"UpdateCategoryDto":{"type":"object","properties":{"labelEs":{"type":"string","example":"Alimentos para bebé"},"labelEn":{"type":"string","example":"Baby food"},"parentSlug":{"type":"object","example":"food","nullable":true,"description":"Parent category slug, or null for a top-level category"},"vertical":{"type":"string","example":"general"},"sort":{"type":"number","example":140},"translations":{"description":"Replace the category_translation rows with this set","type":"array","items":{"$ref":"#/components/schemas/CategoryTranslationDto"}},"externalCodes":{"type":"object","additionalProperties":{"type":"string"},"description":"Códigos externos de interop (#398). Si se indica, reemplaza el mapa; omitir lo conserva."},"archived":{"type":"boolean","example":false,"description":"True to hide/archive the category, false to restore it"}}},"AttributeOptionDto":{"type":"object","properties":{"value":{"type":"string","example":"tableta"},"label":{"type":"string","example":"Tableta"}},"required":["value"]},"AttributeDefinitionDto":{"type":"object","properties":{"categorySlug":{"type":"string","example":"medicines"},"key":{"type":"string","example":"principio_activo"},"dataType":{"type":"string","enum":["text","number","integer","boolean","enum","date","quantity"],"example":"text"},"required":{"type":"boolean","example":false},"options":{"nullable":true,"description":"Opciones del enum, o null","type":"array","items":{"$ref":"#/components/schemas/AttributeOptionDto"}},"unit":{"type":"string","example":null,"nullable":true},"sort":{"type":"number","example":0},"archivedAt":{"type":"object","example":null,"nullable":true,"description":"Marca de archivado (soft-delete)"}},"required":["categorySlug","key","dataType","required","options","unit","sort","archivedAt"]},"CreateAttributeDefinitionDto":{"type":"object","properties":{"categorySlug":{"type":"string","example":"medicines","description":"Slug de la categoría (familia) a la que se ancla"},"key":{"type":"string","example":"principio_activo","description":"Clave del atributo (lowercase snake_case)"},"dataType":{"type":"string","enum":["text","number","integer","boolean","enum","date","quantity"],"example":"text"},"required":{"type":"boolean","example":false,"default":false},"options":{"description":"Opciones (sólo para dataType=enum)","type":"array","items":{"$ref":"#/components/schemas/AttributeOptionDto"}},"unit":{"type":"object","example":"mg","nullable":true,"description":"Unidad (sólo para dataType=number|quantity)"},"sort":{"type":"number","example":0,"default":0}},"required":["categorySlug","key","dataType"]},"SupplyDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"cf8da6e3-7b91-52ff-8cf7-bbff50786c35"},"code":{"type":"string","example":"WAT-0001"},"name":{"type":"string","example":"Agua potable (botellón 18L)","description":"Nombre resuelto en el locale pedido (fallback a `es`)"},"categorySlug":{"type":"string","example":"food"},"categoryLabel":{"type":"string","example":"Alimentos","description":"Etiqueta de categoría resuelta en el locale pedido"},"defaultUnit":{"type":"string","example":"und","nullable":true},"attributes":{"type":"object","example":{"size":"18L"},"description":"Structured attributes for variants and catalog metadata"},"variantOfId":{"type":"string","format":"uuid","nullable":true,"example":null,"description":"Parent supply id when this item is a variant"},"aliases":{"example":["Agua embotellada","botellón"],"description":"Known aliases for the canonical supply","type":"array","items":{"type":"string"}}},"required":["id","code","name","categorySlug","categoryLabel","attributes","aliases"]},"SupplyTranslationDto":{"type":"object","properties":{"locale":{"type":"string","example":"en"},"name":{"type":"string","example":"Drinking water (1.5L bottle)"}},"required":["locale","name"]},"CreateSupplyDto":{"type":"object","properties":{"name":{"type":"string","example":"Agua potable (botella 1.5L)"},"categorySlug":{"type":"string","example":"water","description":"Slug de categoría existente"},"defaultUnit":{"type":"object","example":"und"},"attributes":{"type":"object","additionalProperties":true,"description":"Atributos estructurados (jsonb)"},"registrationNotes":{"type":"object","description":"Notas internas de gestión"},"nature":{"type":"string","enum":["fungible","reusable","human"],"description":"Naturaleza logística (#269): fungible | reusable | human. Omitir = sin clasificar."},"externalCodes":{"type":"object","additionalProperties":{"type":"string"},"description":"Códigos externos estándar para interop (#398): mapa namespace→código, p.ej. { \"unspsc\": \"51101500\", \"hxl\": \"#item+code\" }."},"variantOfId":{"type":"object","format":"uuid","description":"Si es variante, id del insumo padre (debe existir)"},"translations":{"description":"Traducciones del nombre por idioma (i18n). El nombre base es `es`.","type":"array","items":{"$ref":"#/components/schemas/SupplyTranslationDto"}}},"required":["name","categorySlug"]},"CreateSupplyResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","example":"INS-0212"}},"required":["id","code"]},"AdminSupplyDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","example":"INS-0212"},"name":{"type":"string","example":"Agua potable (botella 1.5L)"},"categorySlug":{"type":"string","example":"water"},"defaultUnit":{"type":"string","example":"und","nullable":true},"attributes":{"type":"object","example":{"size":"1.5L"}},"variantOfId":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["active","archived"],"example":"active"},"registrationNotes":{"type":"string","nullable":true},"nature":{"type":"string","enum":["fungible","reusable","human"],"nullable":true,"example":"fungible","description":"Naturaleza logística (#269): fungible | reusable | human. Null = sin clasificar."},"externalCodes":{"type":"object","additionalProperties":{"type":"string"},"description":"Códigos externos estándar para interop (#398): mapa namespace→código. `{}` si no tiene.","example":{"unspsc":"51101500","hxl":"#item+code"}},"aliases":{"example":["agua embotellada","botellon"],"type":"array","items":{"type":"string"}},"translations":{"description":"Traducciones del nombre por idioma (i18n, #320)","example":[{"locale":"en","name":"Drinking water (1.5L bottle)"}],"type":"array","items":{"$ref":"#/components/schemas/SupplyTranslationDto"}}},"required":["id","code","name","categorySlug","attributes","status","externalCodes","aliases","translations"]},"UnmatchedSupplyLineGroupDto":{"type":"object","properties":{"name":{"type":"string","example":"Harina PAN"},"lines":{"type":"number","example":12,"description":"Líneas sin enlazar con ese texto"},"sources":{"type":"array","items":{"type":"string","enum":["need_items","offer_items","resource_items","donation_intake_lines","container_lines"]}},"ambiguous":{"type":"boolean","example":false,"description":"true = el texto casa con varios insumos activos: fusionar duplicados en vez de crear un alias"}},"required":["name","lines","sources","ambiguous"]},"SupplyLinkReportDto":{"type":"object","properties":{"unmatchedLines":{"type":"number","example":27},"unmatched":{"type":"array","items":{"$ref":"#/components/schemas/UnmatchedSupplyLineGroupDto"}},"pendingNames":{"type":"number","example":34,"description":"Textos distintos que un backfill enlazaría ahora"},"pendingLines":{"type":"number","example":120}},"required":["unmatchedLines","unmatched","pendingNames","pendingLines"]},"SupplyLinkBackfillResultDto":{"type":"object","properties":{"unmatchedLines":{"type":"number","example":27},"unmatched":{"type":"array","items":{"$ref":"#/components/schemas/UnmatchedSupplyLineGroupDto"}},"linkedNames":{"type":"number","example":34,"description":"Textos distintos que casaron contra el catálogo"},"linkedLines":{"type":"number","example":120,"description":"Líneas realmente actualizadas en esta ejecución (0 al re-correr o si otra ejecución concurrente las enlazó antes)"}},"required":["unmatchedLines","unmatched","linkedNames","linkedLines"]},"EditSupplyDto":{"type":"object","properties":{"name":{"type":"string","example":"Agua mineral"},"categorySlug":{"type":"string","example":"food"},"defaultUnit":{"type":"object","example":"kg"},"attributes":{"type":"object","additionalProperties":true},"registrationNotes":{"type":"object"},"nature":{"type":"string","enum":["fungible","reusable","human"],"nullable":true,"description":"Naturaleza logística (#269). `null` la limpia (sin clasificar); omitir no la toca."},"externalCodes":{"type":"object","additionalProperties":{"type":"string"},"description":"Códigos externos de interop (#398). Si se indica, reemplaza el mapa completo; `{}` los limpia."},"variantOfId":{"type":"object","format":"uuid"},"translations":{"description":"Reemplaza el conjunto de traducciones del insumo; omitir para no tocarlas.","type":"array","items":{"$ref":"#/components/schemas/SupplyTranslationDto"}}}},"AddSupplyAliasDto":{"type":"object","properties":{"term":{"type":"string","example":"agua embotellada","description":"Término/sinónimo; se normaliza en el servidor"}},"required":["term"]},"MergeSuppliesDto":{"type":"object","properties":{"sourceId":{"type":"string","format":"uuid","description":"Insumo duplicado (se archiva)"},"targetId":{"type":"string","format":"uuid","description":"Insumo canónico (conserva todo)"}},"required":["sourceId","targetId"]},"ContainerHolderDto":{"type":"object","properties":{"type":{"type":"string","enum":["resource","shipment","vehicle"],"example":"resource"},"id":{"type":"string","format":"uuid","description":"Resource node or shipment id (polymorphic, no FK)"}},"required":["type","id"]},"CreateContainerDto":{"type":"object","properties":{"emergencyId":{"type":"string","format":"uuid","description":"Emergency this container serves"},"type":{"type":"string","enum":["pallet","box","lote"],"example":"pallet"},"lines":{"description":"Optional initial loose supply lines","type":"array","items":{"$ref":"#/components/schemas/SupplyLineDto"}},"grossWeightKg":{"type":"number","example":120,"description":"Declared gross weight in kg (positive)"},"grossVolumeM3":{"type":"number","example":1.2,"description":"Declared gross volume in m³ (positive)"},"holder":{"description":"Optional initial holder (e.g. the hub it is created at)","allOf":[{"$ref":"#/components/schemas/ContainerHolderDto"}]}},"required":["emergencyId","type"]},"CreateContainerResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","example":"PAL-0001","description":"Generated trackable code"}},"required":["id","code"]},"NestContainerDto":{"type":"object","properties":{"parentContainerId":{"type":"string","format":"uuid","nullable":true,"description":"New parent container, or null to un-nest (make it top-level)"}}},"MoveContainerDto":{"type":"object","properties":{"holder":{"nullable":true,"description":"New holder (resource ↔ shipment), or null to detach","type":"object","allOf":[{"$ref":"#/components/schemas/ContainerHolderDto"}]}}},"ContainerTreeViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","example":"PAL-0001"},"type":{"type":"string","enum":["pallet","box","lote"],"example":"pallet"},"emergencyId":{"type":"string","format":"uuid"},"parentContainerId":{"type":"string","format":"uuid","nullable":true},"lines":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}},"grossWeightKg":{"type":"number","example":120,"nullable":true},"grossVolumeM3":{"type":"number","example":1.2,"nullable":true},"holderType":{"type":"string","enum":["resource","shipment","vehicle"],"nullable":true,"example":"resource"},"holderId":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["open","sealed"],"example":"open"},"createdAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"},"updatedAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"},"children":{"description":"Direct children (recursive)","type":"array","items":{"$ref":"#/components/schemas/ContainerTreeViewDto"}},"totalWeightKg":{"type":"number","example":145,"nullable":true,"description":"Aggregated weight (own + Σ children), null if none declared"},"totalVolumeM3":{"type":"number","example":2.5,"nullable":true,"description":"Aggregated volume (own + Σ children), null if none declared"}},"required":["id","code","type","emergencyId","lines","status","createdAt","updatedAt","children"]},"ContainerViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","example":"PAL-0001"},"type":{"type":"string","enum":["pallet","box","lote"],"example":"pallet"},"emergencyId":{"type":"string","format":"uuid"},"parentContainerId":{"type":"string","format":"uuid","nullable":true},"lines":{"type":"array","items":{"$ref":"#/components/schemas/SupplyLineResponseDto"}},"grossWeightKg":{"type":"number","example":120,"nullable":true},"grossVolumeM3":{"type":"number","example":1.2,"nullable":true},"holderType":{"type":"string","enum":["resource","shipment","vehicle"],"nullable":true,"example":"resource"},"holderId":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["open","sealed"],"example":"open"},"createdAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"},"updatedAt":{"type":"string","example":"2026-07-01T00:00:00.000Z"}},"required":["id","code","type","emergencyId","lines","status","createdAt","updatedAt"]},"RegisterVolunteerDto":{"type":"object","properties":{"name":{"type":"string","example":"Ana García","minLength":2},"contact":{"type":"string","example":"ana@example.com"},"municipality":{"type":"string","example":"Valencia"},"skills":{"type":"array","example":["medical","driving"],"items":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"]}},"availability":{"type":"string","enum":["immediate","this_week","flexible"],"example":"immediate"},"vehicle":{"type":"string","enum":["none","car","van","truck"],"example":"car"},"consentAccepted":{"type":"boolean","example":true,"description":"Must be true to register as a volunteer"}},"required":["name","contact","municipality","skills","availability","vehicle","consentAccepted"]},"RegisterVolunteerResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]},"VolunteerViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"name":{"type":"string"},"contact":{"type":"string"},"municipality":{"type":"string"},"skills":{"type":"array","items":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"]}},"availability":{"type":"string","enum":["immediate","this_week","flexible"]},"vehicle":{"type":"string","enum":["none","car","van","truck"]},"status":{"type":"string","enum":["available","assigned","inactive"]},"consentAccepted":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","emergencyId","userId","name","contact","municipality","skills","availability","vehicle","status","consentAccepted","createdAt","updatedAt"]},"UpdateVolunteerStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["available","assigned","inactive"],"example":"assigned"}},"required":["status"]},"CreateTaskDto":{"type":"object","properties":{"title":{"type":"string","example":"Deliver supplies to zone 3"},"description":{"type":"string","example":"Transport 200 water bottles to the distribution point"},"location":{"$ref":"#/components/schemas/LocationDto"},"requiredSkill":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"]}},"required":["title","description"]},"CreateTaskResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]},"TaskLocationDto":{"type":"object","properties":{"address":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["address","latitude","longitude"]},"TaskAssignmentViewDto":{"type":"object","properties":{"volunteerId":{"type":"string","format":"uuid"},"volunteerName":{"type":"string","nullable":true},"assignedAt":{"format":"date-time","type":"string"},"checkedInAt":{"type":"string","nullable":true,"format":"date-time"},"checkedOutAt":{"type":"string","nullable":true,"format":"date-time"},"status":{"type":"string","enum":["assigned","checked_in","checked_out"]}},"required":["volunteerId","assignedAt","status"]},"TaskViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string"},"location":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/TaskLocationDto"}]},"requiredSkill":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"],"nullable":true},"status":{"type":"string","enum":["open","in_progress","completed","cancelled"]},"createdByUserId":{"type":"string","format":"uuid"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"assignments":{"type":"array","items":{"$ref":"#/components/schemas/TaskAssignmentViewDto"}}},"required":["id","emergencyId","title","description","status","createdByUserId","createdAt","updatedAt","assignments"]},"MyTaskViewDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"emergencyId":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string"},"location":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/TaskLocationDto"}]},"requiredSkill":{"type":"string","enum":["driving","medical","logistics","cooking","languages","admin","general"],"nullable":true},"status":{"type":"string","enum":["open","in_progress","completed","cancelled"]},"createdByUserId":{"type":"string","format":"uuid"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"assignments":{"type":"array","items":{"$ref":"#/components/schemas/TaskAssignmentViewDto"}},"myAssignmentStatus":{"type":"string","enum":["assigned","checked_in","checked_out"]}},"required":["id","emergencyId","title","description","status","createdByUserId","createdAt","updatedAt","assignments","myAssignmentStatus"]},"AssignVolunteerDto":{"type":"object","properties":{"volunteerId":{"type":"string","format":"uuid"}},"required":["volunteerId"]},"SubmitReportDto":{"type":"object","properties":{"type":{"type":"string","enum":["incident","stock","status","other"]},"note":{"type":"string","example":"Road blocked near bridge"},"priority":{"type":"string","enum":["low","medium","high","urgent"]},"photoUrls":{"description":"URLs from POST /files (e.g. /files/key.png)","type":"array","items":{"type":"string"}},"resourceId":{"type":"string","description":"Resource ID this report refers to"},"location":{"$ref":"#/components/schemas/LocationDto"}},"required":["type","note","priority"]},"EditReportDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo de la edición (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Se corrige la prioridad y se completa la nota"},"note":{"type":"string","description":"Nueva nota (omitir para no cambiarla)","minLength":2},"priority":{"type":"string","enum":["low","medium","high","urgent"],"description":"Nueva prioridad"}},"required":["reason"]},"DiscardReportDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo del descarte (obligatorio, para trazabilidad)","minLength":3,"maxLength":1000,"example":"Reporte duplicado; ya registrado en otra entrada"}},"required":["reason"]},"AuditChangeDto":{"type":"object","properties":{"field":{"type":"string","description":"Name of the field that changed"},"before":{"type":"object","description":"Value before the change (any JSON type)","nullable":true},"after":{"type":"object","description":"Value after the change (any JSON type)","nullable":true}},"required":["field"]},"AuditEntryDto":{"type":"object","properties":{"id":{"type":"string"},"actorUserId":{"type":"string","nullable":true},"actorName":{"type":"string","nullable":true,"description":"Actor display name captured at write time"},"action":{"type":"string"},"entityType":{"type":"string","nullable":true},"entityId":{"type":"string","nullable":true},"emergencyId":{"type":"string","nullable":true},"method":{"type":"string"},"path":{"type":"string"},"statusCode":{"type":"number"},"reason":{"type":"string","nullable":true,"description":"Mandatory reason for edit/discard actions"},"changes":{"nullable":true,"description":"Before/after field changes for edit actions","type":"array","items":{"$ref":"#/components/schemas/AuditChangeDto"}},"targetStatus":{"type":"string","nullable":true,"description":"State the entity transitioned to (e.g. rejected)"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","action","method","path","statusCode","createdAt"]},"AuditListResponseDto":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntryDto"}},"total":{"type":"number"}},"required":["entries","total"]},"MyGroupResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"visibility":{"type":"string","enum":["public","private"]},"ownerKind":{"type":"string","enum":["organization","emergency"]},"ownerId":{"type":"string","format":"uuid"},"parentGroupId":{"type":"object","format":"uuid","nullable":true},"createdAt":{"type":"string"},"membershipStatus":{"type":"string","enum":["pending","approved"]}},"required":["id","name","visibility","ownerKind","ownerId","parentGroupId","createdAt","membershipStatus"]},"GroupResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"visibility":{"type":"string","enum":["public","private"]},"ownerKind":{"type":"string","enum":["organization","emergency"]},"ownerId":{"type":"string","format":"uuid"},"parentGroupId":{"type":"object","format":"uuid","nullable":true},"createdAt":{"type":"string"}},"required":["id","name","visibility","ownerKind","ownerId","parentGroupId","createdAt"]},"CreateGroupDto":{"type":"object","properties":{"name":{"type":"string","description":"Group/cuadrilla name"},"visibility":{"type":"string","enum":["public","private"]},"ownerKind":{"type":"string","enum":["organization","emergency"],"description":"Whether the group hangs off an organization or an emergency"},"ownerId":{"type":"string","format":"uuid","description":"Id of the owning org/emergency"},"parentGroupId":{"type":"string","format":"uuid","description":"Parent group (nesting)"}},"required":["name","visibility","ownerKind","ownerId"]},"IdResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]},"GroupMemberResponseDto":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved"]},"addedByUserId":{"type":"object","format":"uuid","nullable":true}},"required":["userId","status","addedByUserId"]},"AddMemberByEmailDto":{"type":"object","properties":{"email":{"type":"string","description":"Email of the user to add (must already exist)"}},"required":["email"]},"AssignManagerDto":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Member to appoint as manager"}},"required":["userId"]}}}}