# Requirements Document

## Introduction

The Event CRM Contacts feature provides a lightweight, mobile-friendly tool for capturing and managing people met at an event. The system consists of a Laravel RESTful API backend that manages a single `Contact` resource and a Vue single-page frontend that consumes the API. The user will be able to quickly add a new contact, edit existing contacts, browse all contacts in a list, and search or filter the list to find someone quickly. The feature is intentionally scoped to a single core entity to keep capture fast at the event while still supporting later querying and editing.

## Glossary

- **API**: The Laravel RESTful HTTP interface exposed under the `/api/contacts` URL prefix that returns JSON responses.
- **Backend**: The Laravel 13 application running on PHP 8.3 that hosts the API and persists data to the configured MySQL database.
- **Contact**: A single person captured by the user, represented by one row in the `contacts` database table and exposed through the API as a JSON object.
- **Contact_Controller**: The Laravel HTTP controller that handles the REST endpoints for the Contact resource.
- **Contact_Form**: The Vue component used to enter or edit a Contact's fields.
- **Contact_List_View**: The Vue component that renders all Contacts as a table or list, including the search and filter controls.
- **Frontend**: The Vue 3 single-page application bundled by Vite and served from the Laravel `resources/js` directory.
- **Notes**: A free-text field on a Contact used to capture meeting context, such as where the contact was met or topics discussed.
- **Search_Query**: A free-text string supplied by the user that is matched against indexed Contact fields to filter the list.
- **System**: The combined Backend and Frontend acting together to deliver the Event CRM Contacts feature.
- **Tag**: A short string label attached to a Contact, stored as part of a list of tags on the Contact, used for filtering.
- **Tag_Filter**: A trimmed, non-empty `tag` query parameter value used to filter Contacts by their tag list.
- **User**: The single operator using the application at an event to capture and manage Contacts. Authentication is out of scope for this feature.
- **Validation_Error**: A structured JSON response with HTTP status 422 containing per-field error messages, produced by the Backend when input fails validation.

## Requirements

### Requirement 1: Contact Data Model

**User Story:** As a User, I want each Contact to capture the essential details of a person I met, so that I can recognise and follow up with them later.

#### Acceptance Criteria

1. THE Backend SHALL persist each Contact with the fields `id`, `name`, `email`, `phone`, `company`, `role`, `notes`, `tags`, `created_at`, and `updated_at`, where `id` is a server-generated unique identifier that is immutable after creation.
2. THE Backend SHALL store `name` as a required string of 1 to 120 characters after trimming leading and trailing whitespace.
3. THE Backend SHALL store `email` as an optional string of up to 254 characters that, when provided, matches the format `local-part@domain` with exactly one `@` separator, a non-empty local-part, and a domain containing at least one dot and no whitespace.
4. THE Backend SHALL store `phone` as an optional string of up to 32 characters containing only digits, spaces, and the characters `+`, `-`, `(`, `)`.
5. THE Backend SHALL store `company` as an optional string of up to 120 characters.
6. THE Backend SHALL store `role` as an optional string of up to 120 characters.
7. THE Backend SHALL store `notes` as an optional text field of up to 2000 characters.
8. THE Backend SHALL store `tags` as a list of zero or more non-empty string labels, each between 1 and 32 characters after trimming, with no duplicate labels (case-insensitive comparison) and at most 10 tags per Contact.
9. THE Backend SHALL set `created_at` to the server time at insertion and `updated_at` to the server time at insertion or last modification, both stored as UTC timestamps with millisecond precision.
10. IF any field fails the validation rules in criteria 2 through 8, THEN THE Backend SHALL reject the Contact write operation, return a validation error response identifying each invalid field and its violated rule, and leave the persisted state of every Contact in storage (including the targeted Contact and all other Contacts) unchanged.

### Requirement 2: Create a Contact via the API

**User Story:** As a User, I want to add a new Contact through the API, so that the Frontend can capture people I meet at the event.

#### Acceptance Criteria

1. WHEN a `POST /api/contacts` request is received with `Content-Type: application/json` and a JSON body that satisfies every field rule defined in Requirement 1, THE Contact_Controller SHALL persist a new Contact and return HTTP status 201 with the created Contact as JSON.
2. WHEN the Contact_Controller returns HTTP status 201 for a `POST /api/contacts` request, THE Contact_Controller SHALL include in the response body the assigned `id`, the persisted `name`, `email`, `phone`, `company`, `role`, `notes`, and `tags` values, and the `created_at` and `updated_at` timestamps.
3. IF a `POST /api/contacts` request body omits `name`, contains a `name` that is not a string, or contains a `name` that is empty after trimming, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying the `name` field and SHALL NOT persist a Contact.
4. IF a `POST /api/contacts` request body contains an `email` that is not a string or is not in a valid email format as defined in Requirement 1.3, or exceeds 254 characters, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying the `email` field and SHALL NOT persist a Contact.
5. IF a `POST /api/contacts` request body contains a `name`, `phone`, `company`, `role`, or `notes` field whose value is not a string or whose length exceeds the limit defined in Requirement 1, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying that field and SHALL NOT persist a Contact.
6. IF a `POST /api/contacts` request body contains a `tags` value that is not a list of strings, contains more than 10 entries, or contains an entry that is empty after trimming or exceeds 32 characters, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying the `tags` field and SHALL NOT persist a Contact.
7. WHEN one or more validation failures from criteria 3 through 6 apply to the same `POST /api/contacts` request, THE Contact_Controller SHALL return a single HTTP status 422 response containing exactly one Validation_Error entry per invalid field.
8. IF a `POST /api/contacts` request body includes the server-managed fields `id`, `created_at`, or `updated_at`, THEN THE Contact_Controller SHALL ignore those values and SHALL assign them on the server.
9. WHEN the Contact_Controller returns HTTP status 422 for a `POST /api/contacts` request, THE response body SHALL NOT include any Contact payload data and SHALL contain only Validation_Error entries.

### Requirement 3: Read a Single Contact via the API

**User Story:** As a User, I want to retrieve one Contact by its identifier, so that the Frontend can display or edit its details.

#### Acceptance Criteria

1. WHEN a `GET /api/contacts/{id}` request is received with a syntactically valid `id` and a Contact with that `id` exists, THE Contact_Controller SHALL return HTTP status 200 within 1000 milliseconds with a JSON response body containing the Contact's currently stored `id`, `name`, `email`, `phone`, `company`, `role`, `notes`, `tags`, `created_at`, and `updated_at` values, where `created_at` and `updated_at` are ISO 8601 timestamps in UTC.
2. IF a `GET /api/contacts/{id}` request is received with a syntactically valid `id` and no Contact with that `id` exists, THEN THE Contact_Controller SHALL return HTTP status 404 with a JSON error body containing a `message` field, and SHALL NOT return HTTP status 200 with an error payload.
3. IF a `GET /api/contacts/{id}` request is received where `{id}` does not conform to the Contact identifier format defined in the data model, THEN THE Contact_Controller SHALL return HTTP status 400 with a JSON error body containing a `message` field, and SHALL NOT perform a Contact lookup.
4. WHEN a `GET /api/contacts/{id}` request is processed, THE Contact_Controller SHALL NOT modify any stored Contact field of the targeted Contact or any other Contact, including content fields, `updated_at`, and any metadata fields such as access or analytics timestamps.
5. IF a `GET /api/contacts/{id}` lookup succeeds but the resulting Contact record is missing any of the fields enumerated in criterion 1, THEN THE Contact_Controller SHALL return HTTP status 500 with a JSON error body containing a `message` field, and SHALL NOT return HTTP status 200 with a partial Contact payload.

### Requirement 4: Update a Contact via the API

**User Story:** As a User, I want to update an existing Contact, so that I can correct details or add notes after the initial capture.

#### Acceptance Criteria

1. WHEN a `PATCH /api/contacts/{id}` request is received with a JSON body containing one or more of the updatable Contact fields defined in Requirement 1 and a Contact with that `id` exists, THE Contact_Controller SHALL apply only the supplied fields to the stored Contact and return HTTP status 200 with the updated Contact as JSON.
2. WHEN a `PUT /api/contacts/{id}` request is received with a JSON body containing every field required by Requirement 1 and a Contact with that `id` exists, THE Contact_Controller SHALL replace each updatable field of the stored Contact with the corresponding value from the body, set each optional field omitted from the body to its empty value (null for string fields, empty list for `tags`), and return HTTP status 200 with the updated Contact as JSON.
3. WHEN an update request changes the stored value of at least one updatable field of the Contact, THE Contact_Controller SHALL set `updated_at` to the server time at which the change is applied.
4. WHEN an update request is received whose resulting field values are identical to the current stored values of the Contact, THE Contact_Controller SHALL leave `updated_at` unchanged and SHALL return HTTP status 200 with the unchanged Contact as JSON.
5. IF an update request is received and no Contact with the supplied `id` exists, THEN THE Contact_Controller SHALL return HTTP status 404 with a JSON error body.
6. IF an update request body contains values that violate the field rules in Requirement 1, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying each invalid field and SHALL NOT persist any of the supplied values to the stored Contact.
7. WHEN a `PATCH /api/contacts/{id}` request omits a field, THE Contact_Controller SHALL leave the corresponding stored value unchanged.
8. IF an update request body includes an `id`, `created_at`, or `updated_at` field, THEN THE Contact_Controller SHALL ignore those values and SHALL NOT modify the stored `id` or `created_at` of the Contact.

### Requirement 5: Delete a Contact via the API

**User Story:** As a User, I want to delete a Contact, so that I can remove duplicates or entries created by mistake.

#### Acceptance Criteria

1. WHEN a `DELETE /api/contacts/{id}` request is received and a Contact with that `id` exists, THE Contact_Controller SHALL remove that Contact from storage and return HTTP status 204 with an empty response body.
2. IF a `DELETE /api/contacts/{id}` request is received and no Contact with that `id` currently exists in storage, THEN THE Contact_Controller SHALL return HTTP status 404 with a JSON error body containing a `message` field.
3. WHEN a Contact has been deleted, THE Contact_Controller SHALL return HTTP status 404 for any subsequent `GET /api/contacts/{id}` request with the same `id` and SHALL NOT include that Contact in the `data` array of any subsequent `GET /api/contacts` response.
4. IF the deletion operation for an existing Contact fails because of a database error, THEN THE Contact_Controller SHALL return HTTP status 500 with a JSON error body containing a `message` field, SHALL retain the targeted Contact in storage with all fields unchanged, and SHALL NOT return HTTP status 204.

### Requirement 6: List Contacts via the API

**User Story:** As a User, I want to list all Contacts through the API, so that the Frontend can show me everyone I have captured.

#### Acceptance Criteria

1. WHEN a `GET /api/contacts` request is received, THE Contact_Controller SHALL return HTTP status 200 with a JSON body containing a `data` array of Contacts and a `meta` object with the integer fields `total`, `per_page`, `current_page`, and `last_page`, where each Contact in `data` exposes the same persisted fields used to create the Contact plus its `id`, `created_at`, and `updated_at` timestamps.
2. THE Contact_Controller SHALL return Contacts ordered by `created_at` descending, with ties broken by `id` descending, when no explicit sort is requested.
3. WHERE the request includes a `per_page` query parameter that is an integer between 1 and 100 inclusive, THE Contact_Controller SHALL return at most that many Contacts in the `data` array and SHALL set `meta.per_page` to that value.
4. WHERE the request omits the `per_page` query parameter, THE Contact_Controller SHALL apply a default page size of 25 and SHALL set `meta.per_page` to 25.
5. WHERE the request includes a `page` query parameter that is an integer greater than or equal to 1, THE Contact_Controller SHALL return the corresponding page of results based on the active `per_page` value and SHALL set `meta.current_page` to that value.
6. WHERE the request omits the `page` query parameter, THE Contact_Controller SHALL return the first page of results and SHALL set `meta.current_page` to 1.
7. IF the request includes a `per_page` value that is not an integer or is outside the range 1 to 100 inclusive, or a `page` value that is not an integer or is less than 1, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying the offending parameter and SHALL NOT return any Contact data.
8. IF the request includes a `page` value greater than `meta.last_page`, THEN THE Contact_Controller SHALL return HTTP status 200 with an empty `data` array, `meta.current_page` equal to the requested page, and `meta.total` reflecting the total stored Contact count.
9. WHEN the `contacts` table is empty or no stored Contacts match the active query parameters, THE Contact_Controller SHALL return HTTP status 200 with an empty `data` array, `meta.total` equal to 0, and `meta.last_page` equal to 1.

### Requirement 7: Search and Filter Contacts via the API

**User Story:** As a User, I want to search and filter the Contact list through the API, so that I can quickly find a specific person at the event.

#### Acceptance Criteria

1. WHERE a `GET /api/contacts` request includes a `q` query parameter whose value, after trimming leading and trailing whitespace, is a non-empty Search_Query of 1 to 120 characters, THE Contact_Controller SHALL return only Contacts whose `name`, `email`, `company`, `role`, or `notes` field contains the Search_Query as a case-insensitive substring.
2. WHERE a `GET /api/contacts` request includes a `tag` query parameter whose value, after trimming leading and trailing whitespace, is a non-empty Tag_Filter of 1 to 32 characters, THE Contact_Controller SHALL return only Contacts whose `tags` list contains an entry equal to the Tag_Filter, compared case-insensitively.
3. WHERE a `GET /api/contacts` request includes a `q` or `tag` query parameter whose value is empty or consists only of whitespace characters after trimming, THE Contact_Controller SHALL treat that parameter as not provided and SHALL NOT apply the corresponding filter.
4. WHERE both `q` and `tag` query parameters are provided with non-empty trimmed values, THE Contact_Controller SHALL return only Contacts that satisfy both filters combined with logical AND.
5. WHEN a `GET /api/contacts` request applies a `q` or `tag` filter, THE Contact_Controller SHALL apply the pagination and ordering rules from Requirement 6 to the filtered result set, and SHALL return an empty `data` collection with pagination metadata reflecting zero matches when no Contact satisfies the filters.
6. IF the trimmed value of the `q` parameter exceeds 120 characters or the trimmed value of the `tag` parameter exceeds 32 characters, THEN THE Contact_Controller SHALL return HTTP status 422 with a Validation_Error identifying the offending parameter and SHALL NOT include a `data` array of filtered Contacts in the response body.

### Requirement 8: Capture a Contact from the Frontend

**User Story:** As a User at an event, I want to add a person to the Frontend through a quick form, so that I can capture them in a few seconds before moving on.

#### Acceptance Criteria

1. THE Frontend SHALL render a Contact_Form with input fields for `name` (1 to 120 characters after trimming), `email` (up to 254 characters), `phone` (up to 32 characters), `company` (up to 120 characters), `role` (up to 120 characters), `notes` (up to 2000 characters), and `tags` (up to 10 tags, each between 1 and 32 characters after trimming).
2. WHEN the User submits a Contact_Form whose `name` field is non-empty after trimming and no prior Contact_Form submission is currently in flight, THE Frontend SHALL send a `POST /api/contacts` request containing the form values as JSON.
3. WHILE a Contact_Form submission is in flight, THE Frontend SHALL disable the submit control of the Contact_Form so that the User cannot trigger a second submission for the same form.
4. WHEN the API responds to a Contact_Form submission with HTTP status 201, THE Frontend SHALL clear the Contact_Form, re-enable its submit control, and add the new Contact to the Contact_List_View without requiring a full page reload.
5. IF the User submits a Contact_Form with a `name` field that is empty after trimming, THEN THE Frontend SHALL display an inline validation message on the `name` field indicating that a name is required, SHALL keep all other entered values in the Contact_Form, and SHALL NOT send a request to the API.
6. IF the User submits a Contact_Form in which any field exceeds the length limits defined in criterion 1, THEN THE Frontend SHALL display an inline validation message on each offending field indicating the maximum allowed length, SHALL retain the values entered by the User, and SHALL NOT send a request to the API.
7. IF the API responds to a Contact_Form submission with HTTP status 422, THEN THE Frontend SHALL display the Validation_Error messages on the corresponding fields of the Contact_Form, SHALL retain the values entered by the User, and SHALL re-enable the submit control of the Contact_Form.
8. IF the API responds to a Contact_Form submission with an HTTP status of 500 or higher, THEN THE Frontend SHALL display an error message that leaves all Contact_Form fields and the submit control interactive, SHALL retain the values entered by the User in the Contact_Form, and SHALL re-enable the submit control of the Contact_Form.
9. IF a Contact_Form submission does not receive an HTTP response from the API within 10 seconds, or the network request fails before a response is received, THEN THE Frontend SHALL display an error message indicating that the request could not be completed, SHALL retain the values entered by the User in the Contact_Form, and SHALL re-enable the submit control of the Contact_Form.

### Requirement 9: Browse Contacts in the Frontend List View

**User Story:** As a User, I want to see all my captured Contacts in a single list, so that I can review who I have met at the event.

#### Acceptance Criteria

1. WHILE the viewport is at least 768 pixels wide, THE Contact_List_View SHALL render each Contact's `name`, `company`, `role`, `email`, `phone`, and `tags` in a tabular row layout.
2. WHILE the viewport is narrower than 768 pixels, THE Contact_List_View SHALL render each Contact's `name`, `company`, `role`, `email`, `phone`, and `tags` as a vertically stacked card.
3. WHEN the Contact_List_View is first displayed, THE Frontend SHALL request the first page of Contacts via `GET /api/contacts` with a page size of 25 Contacts and render the returned Contacts within 3 seconds of receiving a successful response.
4. WHEN a `GET /api/contacts` request from the Contact_List_View completes with a successful response containing zero Contacts, THE Contact_List_View SHALL display an empty-state message indicating that no Contacts match, and SHALL NOT display the empty-state message when the request fails or no response is received.
5. WHEN the User activates a pagination control of the Contact_List_View, THE Frontend SHALL request the corresponding page of 25 Contacts via `GET /api/contacts` and render the returned Contacts within 3 seconds of receiving a successful response.
6. IF a request to `GET /api/contacts` from the Contact_List_View fails with an HTTP status of 400 or higher, fails with a network error, or receives no response within 10 seconds, THEN THE Frontend SHALL display an error message indicating that loading failed without obscuring any already-rendered Contacts AND SHALL provide a retry control that re-issues the same request when activated.
7. IF the error condition from criterion 6 is active for the Contact_List_View, THEN THE Frontend SHALL display only the error message and SHALL NOT display the empty-state message from criterion 4, regardless of whether a previous response returned zero Contacts.
8. WHILE a request to `GET /api/contacts` from the Contact_List_View is in flight, THE Frontend SHALL display a loading indicator and SHALL disable the pagination controls until the response is received or the 10-second timeout elapses.

### Requirement 10: Search and Filter from the Frontend

**User Story:** As a User, I want to search and filter the Contact_List_View, so that I can find a specific person quickly while at the event.

#### Acceptance Criteria

1. THE Contact_List_View SHALL provide a search input bound to the `q` API parameter that accepts between 0 and 120 characters and a tag selector bound to the `tag` API parameter that holds at most one tag value at a time.
2. WHEN the User changes the search input, THE Frontend SHALL send a `GET /api/contacts` request using the current search and tag values once the input has been idle for between 290 and 310 milliseconds, and SHALL ignore any response from a previous request superseded by a newer one.
3. WHEN the User selects or changes the value of the tag selector, THE Frontend SHALL send a `GET /api/contacts` request using the current search and tag values within 100 milliseconds and SHALL NOT apply the search-input debounce delay.
4. WHEN the User clears the search input and the tag selector such that both values are empty, THE Frontend SHALL send a `GET /api/contacts` request without `q` or `tag` parameters and SHALL render the returned Contacts.
5. THE Contact_List_View SHALL render only the Contacts returned by the most recent successful `GET /api/contacts` response, and SHALL display an empty-state indicator when that response contains zero Contacts.
6. IF a `GET /api/contacts` request fails or returns a non-success response, THEN THE Frontend SHALL display an error indicator, SHALL preserve the Contacts rendered from the most recent successful response, and SHALL allow the User to trigger a new request by changing the search input or tag selector.

### Requirement 11: Edit an Existing Contact from the Frontend

**User Story:** As a User, I want to edit a Contact from the list, so that I can update details I learn after the first conversation.

#### Acceptance Criteria

1. WHEN the User selects the edit control for a Contact in the Contact_List_View, THE Frontend SHALL send a `GET /api/contacts/{id}` request and, upon receiving HTTP status 200, SHALL display a Contact_Form pre-populated with the returned field values for that Contact.
2. WHEN the User submits the edit Contact_Form whose `name` field is non-empty after trimming, THE Frontend SHALL send a `PUT /api/contacts/{id}` request with the form values as JSON.
3. WHEN the API responds to an edit submission with HTTP status 200, THE Frontend SHALL update the corresponding Contact in the Contact_List_View with the returned values and SHALL close the edit Contact_Form.
4. IF the API responds to an edit submission with HTTP status 404, THEN THE Frontend SHALL display a non-blocking message indicating that the Contact no longer exists and SHALL remove the Contact from the Contact_List_View.
5. IF the API responds to an edit submission with HTTP status 422, THEN THE Frontend SHALL display the Validation_Error messages on the corresponding fields of the Contact_Form.
6. IF the User submits the edit Contact_Form with an empty `name` field after trimming, THEN THE Frontend SHALL display an inline validation message on the `name` field and SHALL NOT send a request to the API.
7. IF a `GET /api/contacts/{id}` request triggered by selecting the edit control responds with HTTP status 404, THEN THE Frontend SHALL display a non-blocking message indicating that the Contact no longer exists, SHALL NOT display the edit Contact_Form, and SHALL remove the Contact from the Contact_List_View.
8. IF the API responds with an HTTP status of 500 or higher to either the `GET /api/contacts/{id}` request triggered by selecting the edit control or to an edit submission, THEN THE Frontend SHALL display a non-blocking error message and SHALL retain any values currently entered by the User in the Contact_Form.

### Requirement 12: Delete a Contact from the Frontend

**User Story:** As a User, I want to delete a Contact from the list, so that I can remove duplicates or entries created by mistake.

#### Acceptance Criteria

1. WHEN the User activates the delete control for a Contact in the Contact_List_View, THE Frontend SHALL display a modal confirmation prompt that displays the Contact's full name, includes a Confirm action and a Cancel action, and blocks further interaction with the Contact_List_View until one action is selected.
2. WHEN the User selects the Confirm action in the delete prompt, THE Frontend SHALL send a `DELETE /api/contacts/{id}` request with a request timeout of 10 seconds and SHALL disable the delete control for that Contact until the request completes or times out.
3. WHEN the API responds to a delete request with HTTP status 204, THE Frontend SHALL remove the Contact from the Contact_List_View and SHALL display a non-blocking success message that auto-dismisses after 5 seconds.
4. IF the User selects the Cancel action in the delete prompt or dismisses it, THEN THE Frontend SHALL retain the Contact in the Contact_List_View and SHALL NOT send a request to the API.
5. IF the API responds to a delete request with HTTP status 404, THEN THE Frontend SHALL remove the Contact from the Contact_List_View and SHALL display a non-blocking message indicating that the Contact had already been removed, auto-dismissing after 5 seconds.
6. IF the API responds to a delete request with any HTTP status in the 4xx range other than 404 or any status in the 5xx range, or the request fails due to a network error or the 10-second timeout elapses, THEN THE Frontend SHALL retain the Contact in the Contact_List_View, SHALL re-enable the delete control for that Contact, and SHALL display a non-blocking error message indicating that the deletion failed and offering a retry action.

### Requirement 13: API Response Format and Error Handling

**User Story:** As a Frontend developer, I want the API to return predictable JSON responses, so that the Frontend can render data and errors consistently.

#### Acceptance Criteria

1. THE Contact_Controller SHALL set the `Content-Type` response header to `application/json; charset=utf-8` for all responses with a non-empty body.
2. THE Contact_Controller SHALL serialise each Contact using the field names defined in Requirement 1, including the `tags` field as a JSON array of strings preserving the order in which they were stored.
3. THE Contact_Controller SHALL serialise `created_at` and `updated_at` as ISO 8601 date-time strings in UTC using the extended format `YYYY-MM-DDTHH:MM:SSZ` (seconds precision, trailing `Z` for UTC).
4. IF an incoming request body is present and is not syntactically valid JSON, regardless of whether the endpoint requires a body, THEN THE Contact_Controller SHALL reject the request before any handler logic executes, return HTTP status 400 with a JSON object body containing a non-empty `message` field of type string describing the parse failure, and SHALL NOT apply any state changes to stored Contacts.
5. IF an unhandled server error occurs while processing a request, THEN THE Contact_Controller SHALL return HTTP status 500 with a JSON object body containing a non-empty `message` field of type string, and SHALL NOT include internal implementation details such as stack traces, file paths, or framework exception class names.
6. THE Contact_Controller SHALL ensure every error response body is a single JSON object that contains at minimum a `message` field of type string with length between 1 and 500 characters, and SHALL NOT include any Contact payload data in the same response.
