Create new address
POST/company/address/
Create a new address associated with the authenticated user's company.
This endpoint allows registering physical locations such as warehouses, offices, or loading/unloading points.
Operation flow:
- Authentication via valid JWT
- Validation of mandatory fields and formats
- Processing of Google Maps data (reverse geocoding)
- If isDefault=true, unmark any existing primary address
- Persistence in the database
- Return of the created address with its ID
Important validations:
- Requires a valid JWT token with admin/editor permissions
- Mandatory 'name' field (3-100 characters)
- Google Maps data must include:
- formatted_address: Complete formatted address
- geometry.location: Coordinates {lat, lng}
- If isDefault=true, unmark any existing primary address
Typical use cases:
- Register a new company headquarters
- Add a logistics warehouse
- Create a pickup point for shipments
Important note about phones:
- Phone numbers (phone) must have a valid format for Spain, France, or Portugal
Requires an active subscription (validated by middleware isPaymentUpdate)
Timestamps are converted to UTC by middleware checkUTC
Example request:
POST /company/address
Authorization: Bearer \{token\}
Content-Type: application/json
\{
name: Logistics Warehouse,
company_name: CargoOffer SL,
phone: +34912345678,
addressGoogleMaps: \{
formatted_address: Calle de la Logística, 123, 28045 Madrid, España,
geometry: \{
location: \{
lat: 40.123456,
lng: -3.987654
\}
\}
\},
isDefault: false
\}
Example successful response:
\{
_id: 507f1f77bcf86cd799439011,
name: Logistics Warehouse,
company_name: CargoOffer SL,
phone: +34912345678,
street: Calle de la Logística, 123,
city: Madrid,
<Heading
id={"request"}
as={"h2"}
className={"openapi-tabs__heading"}
children={"Request"}
>
</Heading>
<ParamsDetails
parameters={[]}
>
</ParamsDetails>
<RequestSchema
title={"Body"}
body={{"content":{"application/json":{"schema":{"properties":{},"type":"object","description":"Data required to create a new address","required":["name","addressGoogleMaps"],"example":{"_id":"507f1f77bcf86cd799439011","name":"Almacén Logístico","company_name":"CargoOffer SL","phone":"+34912345678","addressGoogleMaps":{"formatted_address":"Calle de la Logística, 123, 28045 Madrid, España","geometry":{"location":{"lat":40.123456,"lng":-3.987654}}},"is_default":false},"title":"CreateAddressRequest"},"example":{"name":"Almacén Logístico","company_name":"CargoOffer SL","phone":"+34912345678","addressGoogleMaps":{"formatted_address":"Calle de la Logística, 123, 28045 Madrid, España","geometry":{"location":{"lat":40.123456,"lng":-3.987654}}},"is_default":false}}},"required":true}}
>
</RequestSchema>
<StatusCodes
id={undefined}
label={undefined}
responses={{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"object","description":"Represents a physical address associated with a company for loading/unloading operations.\n**Functionality**: - Origin point (ETL - Expected Time of Loading) or destination (ETD - Expected Time of Delivery) in transport auctions - Stored per company and reusable across multiple operations - Automatically geocoded via Google Maps API upon creation - Validated against use in active auctions/deliveries before deletion\n**Model**: `src/features/models/address.model.js`\n**Controller**: `src/features/company/address/controller.js`","properties":{"_id":{"type":"string","description":"MongoDB unique identifier of the address (24 hexadecimal characters). Automatically generated by the system when the address is created. Used as a reference in auction models (auction.etl_address, auction.etd_address) and delivery models (delivery.etl_address, delivery.etd_address).","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","description":"Descriptive name or custom alias for the location assigned by the user. Used for quick identification in lists and address selection. **Required** - Minimum 3 characters, maximum 100. Examples: North Warehouse, Head Office, Client ABC - Madrid Plant","minLength":3,"maxLength":100,"example":"Oficina Central"},"company_name":{"type":"string","description":"Legal or trade name of the company at this location. **Required** - Appears on official documents (CMR, contracts). May differ from the main company name if it is a customer/supplier address. Minimum 2 characters, maximum 100.","minLength":2,"maxLength":100,"example":"CargoOffer SL"},"phone":{"type":"string","description":"Contact phone number for logistics coordination at this address. **Optional** - Recommended international format (E.164 with +country code). Used by carriers to confirm arrival and resolve incidents. Validated with pattern: 9-15 digits.","pattern":"^\\+?[0-9]{9,15}$","maxLength":20,"example":"+34912345678"},"street":{"type":"string","description":"Full street with number, automatically generated from addressGoogleMaps. Format: Street Name, Number. Used in display and CSV exports. Legacy combined field (see street_address + street_number for separate fields).","example":"Calle Ejemplo 123"},"street_address":{"type":"string","description":"Street name without number (separate field). Parsed from Google Maps API response (route component).","example":"Calle de Alcalá"},"street_number":{"type":"string","description":"Street number as an independent field. Parsed from the Google Maps API response (street_number component).","example":"42"},"city":{"type":"string","description":"City normalized in lowercase, automatically extracted from coordinates via Google Maps Geocoding API. Used in search filters and grouping addresses by area. Type: Google Maps locality or administrative_area_level_2.","example":"madrid"},"state":{"type":"string","description":"State, autonomous community, or administrative region (normalized in lowercase). Extracted from Google Maps' administrative_area_level_1. Optional - May be empty in countries without regional divisions.","example":"comunidad de madrid"},"zipcode":{"type":"string","description":"Postal code in the format of the corresponding country. Extracted from the postal_code component of the Google Maps API. Used for zone validations and fare calculations.","example":"28045"},"country":{"type":"string","description":"ISO 3166-1 alpha-2 country code in UPPERCASE (2 letters). **Critical** for: TaxID validation, license plate format, fee calculation, ADR restrictions. Extracted from the country component (short_name) of the Google Maps API. Must match active countries in the 'countries' collection.","pattern":"^[A-Z]{2}$","example":"ES"},"neighborhood":{"type":"string","description":"Neighborhood or area within the city (optional). Extracted from Google Maps' neighborhood or sublocality. Used for precision in large urban areas.","example":"centro"},"province":{"type":"string","description":"Province or provincial administrative division. Extracted from administrative_area_level_2 in Google Maps (in countries with provinces). Optional - Primarily relevant in Spain, Italy, etc.","example":"madrid"},"location":{"type":"object","description":"Geographic coordinates in GeoJSON Point format according to RFC 7946 standard. Reference system: WGS84 (EPSG:4326). **REQUIRED** - Used by MongoDB for geospatial queries ($near, $geoWithin). **CRITICAL**: Order in the coordinates array is [longitude, latitude] (X, Y) - DO NOT invert. Used for: distance calculations, optimal routing, service area validation.","required":["type","coordinates"],"properties":{"type":{"type":"string","enum":["Point"],"description":"GeoJSON geometry type. Must always be Point for addresses. Other types (LineString, Polygon) are not valid in this context.","example":"Point"},"coordinates":{"type":"array","description":"Array of two numbers: [longitude, latitude] in decimal degrees. **CRITICAL ORDER**: longitude first (X-axis, -180 to 180), latitude second (Y-axis, -90 to 90). Valid example: [-3.70379, 40.416775] = 3.70° West, 40.41° North (Madrid). **COMMON ERROR**: Reversing the order causes incorrect calculations. Used in: model.find({location: {$near: {$geometry: {type: 'Point', coordinates: [lng, lat]}}}})","items":{"type":"number"},"minItems":2,"maxItems":2,"example":[-3.70379,40.416775]}}},"placeId":{"type":"string","description":"Unique and immutable Google Place ID for this location. Used for: reverse geocoding, change validation, fetching updated details. Format: Alphanumeric string typically beginning with ChIJ. Persisted to avoid repeated geocoding (API cost savings).","example":"ChIJi-AoYTIoQg0RnHvWosEVABQ"},"name_address":{"type":"string","description":"Complete formatted address generated by Google Maps (formatted_address). Includes: street, number, postal code, city, country in local format. Used in: user display, exports, official documents. Not manually editable - regenerated automatically when coordinates are updated.","example":"Calle de Alcalá, 42, 28014 Madrid, España"},"isDefault":{"type":"boolean","description":"Indicates whether this is the company's primary/default address. **Constraint**: There can only be one address with isDefault=true per company. When setting isDefault=true on an address, the previous address with this flag is automatically set to false. Used in: Form autocompletion, default address for new auctions. Also stored in: company.address_default (reference to this address's _id).","default":false,"example":true},"can_be_deleted":{"type":"boolean","description":"Flag dynamically calculated at query time (not stored in DB). **false**: The address is referenced in active auctions or deliveries (status != canceled/delivered). **true**: The address has no dependencies and can be safely deleted. Calculated in: controller.get() by checking the count of auction/delivery where etl_address or etd_address == this._id Prevents: accidental deletion of addresses in use, which would cause orphaned data.","example":false},"destinations":{"type":"array","description":"Array of frequent destinations with precalculated routes from this address. **Optimization**: Avoids recalculating repetitive routes, improves performance in auction searches. Populated in: nightly cron or on-demand for frequent address pairs. Used in: /company/minimal endpoint for cost/distance calculations without calling external API. Structure: [{address: ObjectId, minimalRoute: {distance: Number(km), timeCost: Number(min)}}]","items":{"type":"object","properties":{"address":{"type":"string","description":"Destination address ObjectId","example":"507f1f77bcf86cd799439012"},"minimalRoute":{"type":"object","properties":{"distance":{"type":"number","description":"Distance calculated in kilometers (via route API)","example":523.7},"timeCost":{"type":"number","description":"Estimated travel time in minutes (considering average speed)","example":360}}}}}}},"required":["_id","name","location","isDefault","can_be_deleted"],"title":"Address"},"example":{"status":200,"data":{"location":{"type":"Point","coordinates":[0,0]},"phone":"+34912345678","street_number":"","street_address":"","_id":"68f9f8bf0e38c035ea821e58","deleted":false,"destinations":[],"name":"Almacén Logístico","company_name":"CargoOffer SL","createdAt":"2025-10-23T09:43:27.719Z"}}}},"headers":{}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["MISSING_FIELD_NAME","INVALID_GOOGLE_MAPS_DATA","INVALID_ADDRESS_FORMAT"],"example":"INVALID_GOOGLE_MAPS_DATA"}}}}},"headers":{}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}},"headers":{}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}},"headers":{}}}}
>
</StatusCodes>