# How to Configure Asset Locations in Snipe-IT: A Complete Setup Guide

> Configure asset locations in Snipe-IT using the Location model. Learn to track physical sites and deploy positions with hierarchical relationships and API support.

- Repository: [Grokability, Inc./snipe-it](https://github.com/grokability/snipe-it)
- Tags: how-to-guide
- Published: 2026-07-31

---

**Asset locations in Snipe-IT are configured through the Location model which supports hierarchical parent-child relationships, REST API endpoints, and CSV import functionality, allowing administrators to track physical storage sites and ready-to-deploy positions via the `location_id` and `rtd_location_id` fields.**

The open-source asset management platform [grokability/snipe-it](https://github.com/grokability/snipe-it) provides a flexible location system defined in [`app/Models/Location.php`](https://github.com/grokability/snipe-it/blob/main/app/Models/Location.php) and managed through both web-based controllers and RESTful API endpoints. Properly configuring these locations ensures accurate asset tracking across multiple sites while supporting Full Multiple Company Support (FMCS) scoping for multi-tenant deployments.


## Understanding the Location Data Model

### The Location Model and Database Schema

Locations are persisted in the `locations` table created by the migration at [`database/migrations/2013_11_16_103258_create_locations_table.php`](https://github.com/grokability/snipe-it/blob/main/database/migrations/2013_11_16_103258_create_locations_table.php). The **Location** Eloquent model ([`app/Models/Location.php`](https://github.com/grokability/snipe-it/blob/main/app/Models/Location.php)) defines the core schema and relationships, including support for hierarchical nesting via the `parent_id` field.

The model implements two critical asset relationships:

- **`assets()`** – Retrieves assets where the `location_id` foreign key matches the location, representing the current physical storage position.
- **`rtdAssets()`** – Retrieves assets where the `rtd_location_id` (ready-to-deploy location) matches the location, indicating where items should be stocked when not checked out.

These relationships are maintained by the `MigratesLegacyAssetLocations` trait found in [`app/Http/Traits/MigratesLegacyAssetLocations.php`](https://github.com/grokability/snipe-it/blob/main/app/Http/Traits/MigratesLegacyAssetLocations.php), which ensures backward compatibility when upgrading from older Snipe-IT versions that handled location data differently.


## Creating and Managing Locations

### Via the Web Interface

The UI controller at [`app/Http/Controllers/LocationsController.php`](https://github.com/grokability/snipe-it/blob/main/app/Http/Controllers/LocationsController.php) handles form validation and CRUD operations for location management. Administrators can access this through **Admin → Locations** in the navigation menu. The controller uses the `LocationPresenter` class ([`app/Presenters/LocationPresenter.php`](https://github.com/grokability/snipe-it/blob/main/app/Presenters/LocationPresenter.php)) to generate human-readable paths such as `fullLocationPath()`, which concatenates parent and child location names for display in dropdown selectors.

### Via the REST API

For programmatic configuration, the API controller at [`app/Http/Controllers/Api/LocationsController.php`](https://github.com/grokability/snipe-it/blob/main/app/Http/Controllers/Api/LocationsController.php) exposes JSON endpoints. The controller utilizes [`app/Http/Transformers/LocationsTransformer.php`](https://github.com/grokability/snipe-it/blob/main/app/Http/Transformers/LocationsTransformer.php) to standardize API responses and respects the authorization policies defined in [`app/Policies/LocationPolicy.php`](https://github.com/grokability/snipe-it/blob/main/app/Policies/LocationPolicy.php).

To create a location via API:

```bash
curl -X POST https://your-domain.com/api/v1/locations \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Building A - Floor 2",
    "address": "123 Tech Street",
    "city": "San Francisco",
    "state": "CA",
    "zip": "94105",
    "country": "US",
    "parent_id": 1
  }'

```

### Bulk Import via CSV

For large-scale deployments, use the command-line importer at [`app/Importer/LocationImporter.php`](https://github.com/grokability/snipe-it/blob/main/app/Importer/LocationImporter.php):

```bash
php artisan snipeit:import:locations /path/to/locations.csv

```

The CSV should contain headers matching the database columns: `name`, `address`, `city`, `state`, `country`, `zip`, and optionally `parent_id` to establish hierarchies during import.


## Assigning Locations to Assets

### The Asset-Location Relationship

When editing an asset, the system updates two distinct fields in the `assets` table:

1. **`location_id`** – The current physical location of the asset
2. **`rtd_location_id`** – The location where the asset should be returned when checked in (Ready-To-Deploy)

These fields are rendered in Blade views using the location selector partial at [`resources/views/partials/forms/edit/location-select.blade.php`](https://github.com/grokability/snipe-it/blob/main/resources/views/partials/forms/edit/location-select.blade.php), which implements Select2 AJAX loading for searchable dropdowns:

```blade
<div class="form-group">
    <label for="location_id">{{ trans('admin/locations/general.location') }}</label>
    <select class="form-control js-data-ajax" 
            data-endpoint="locations" 
            name="location_id" 
            id="location_id">
        <option value="">{{ trans('general.select_location') }}</option>
    </select>
</div>

```

### Hierarchical Locations and Parent IDs

Locations support unlimited nesting through the `parent_id` database column. When `parent_id` is set to another location's ID, the system treats it as a child location. The `LocationPresenter::fullLocationPath()` method traverses this hierarchy to display complete paths like "Main Warehouse > Building B > Room 204" in user interfaces.

To set a parent via API, simply include the `parent_id` parameter:

```php
// Using Artisan Tinker
>>> use App\Models\Location;
>>> $child = Location::create([
...     'name' => 'Server Room B',
...     'parent_id' => 5, // ID of parent location
...     'company_id' => 1 // Required when FMCS is enabled
... ]);

```


## Multi-Company Support and Location Scoping

When **Full Multiple Company Support (FMCS)** is enabled in settings, the [`app/Livewire/LocationScopeCheck.php`](https://github.com/grokability/snipe-it/blob/main/app/Livewire/LocationScopeCheck.php) component enforces company-specific location visibility. This Livewire component filters location dropdowns to show only locations belonging to the user's assigned company or the asset's company context.

The [`app/Policies/LocationPolicy.php`](https://github.com/grokability/snipe-it/blob/main/app/Policies/LocationPolicy.php) file governs authorization, ensuring that users can only view, edit, or delete locations within their permitted scope. When using the Select2 AJAX endpoints, the JavaScript in [`snipeit.js`](https://github.com/grokability/snipe-it/blob/main/snipeit.js) automatically appends the `companyId` parameter to requests, allowing the API controllers to filter results accordingly.


## Common Configuration Pitfalls

- **Empty string handling**: The `Location` model casts empty string inputs to `null` for the `parent_id` field. Ensure your import CSV uses actual null values or valid integer IDs rather than empty strings to avoid validation errors.

- **Asset reassignment on deletion**: Deleting a location via [`LocationsController.php`](https://github.com/grokability/snipe-it/blob/main/LocationsController.php) does not cascade updates to associated assets. The `location_id` and `rtd_location_id` fields in the assets table will retain the deleted location's ID unless you manually reassign assets beforehand or implement custom observers.

- **Performance on deep hierarchies**: Queries against nested location trees perform best when the `parent_id` index is present. Ensure your installation includes the latest migrations that add indexes to the `parent_id` and `company_id` columns in the locations table.


## Summary

- Configure asset locations using the **Location** model at [`app/Models/Location.php`](https://github.com/grokability/snipe-it/blob/main/app/Models/Location.php), which supports hierarchical relationships via `parent_id`
- Manage locations through the UI controller ([`LocationsController.php`](https://github.com/grokability/snipe-it/blob/main/LocationsController.php)), API endpoints ([`Api/LocationsController.php`](https://github.com/grokability/snipe-it/blob/main/Api/LocationsController.php)), or CSV import ([`LocationImporter.php`](https://github.com/grokability/snipe-it/blob/main/LocationImporter.php))
- Distinguish between **`location_id`** (current position) and **`rtd_location_id`** (ready-to-deploy position) when assigning assets
- Enable **FMCS scoping** via [`LocationScopeCheck.php`](https://github.com/grokability/snipe-it/blob/main/LocationScopeCheck.php) and [`LocationPolicy.php`](https://github.com/grokability/snipe-it/blob/main/LocationPolicy.php) for multi-company environments
- Use the **LocationPresenter** class to render full location paths in custom Blade templates


## Frequently Asked Questions

### What is the difference between location_id and rtd_location_id in Snipe-IT?

The `location_id` field stores the asset's current physical location, while `rtd_location_id` (Ready-To-Deploy location) indicates where the asset should be stored when it is checked back in and available for deployment. This dual-field system allows Snipe-IT to track both where an asset is currently located and its designated "home" location separately.

### How do I enable hierarchical locations in Snipe-IT?

Hierarchical locations are supported natively without additional configuration. Simply populate the `parent_id` field when creating a location via the web form at **Admin → Locations → Create New** or via the API by specifying `parent_id` in your POST request. The system automatically renders parent-child relationships in dropdown menus using the `LocationPresenter::fullLocationPath()` method.

### Can I import locations in bulk using a CSV file?

Yes, use the built-in artisan command `php artisan snipeit:import:locations /path/to/file.csv` which processes the CSV through [`app/Importer/LocationImporter.php`](https://github.com/grokability/snipe-it/blob/main/app/Importer/LocationImporter.php). The CSV must include a `name` column and can optionally include `address`, `city`, `state`, `zip`, `country`, and `parent_id` columns to establish the location hierarchy during import.

### Why are assets not appearing in the correct location scope for certain users?

When Full Multiple Company Support (FMCS) is enabled, the `LocationScopeCheck` Livewire component and `LocationPolicy` restrict location visibility based on company assignments. Ensure the location's `company_id` matches the user's company or that the user has permissions to view locations across companies. The AJAX endpoints respect the `companyId` query parameter sent by the Select2 components in [`snipeit.js`](https://github.com/grokability/snipe-it/blob/main/snipeit.js).