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

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 provides a flexible location system defined in 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. The Location Eloquent model (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, 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 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) 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 exposes JSON endpoints. The controller utilizes app/Http/Transformers/LocationsTransformer.php to standardize API responses and respects the authorization policies defined in app/Policies/LocationPolicy.php.

To create a location via API:

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:

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, which implements Select2 AJAX loading for searchable dropdowns:

<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:

// 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 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 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 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 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

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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →