Zones, Regions & Pincodes

The three building blocks AIR uses to describe "where" — a coarse sales Zone, a fine-grained administrative Region tree, and the postal Pincodes that sit under a region. Warehouses, customer addresses and carrier serviceability all point back to these records.

Permissions

All three resources on this page — Zones, Regions and Pincodes — are restricted to super administrators only. The console checks this with a hardcoded "is this user a super admin?" test, not a permission string, so granting someone the seeded zone.view, region.view or pincode.view permissions on a non-super-admin role has no effect — they still will not see these pages. Only the super_admin role can open Geography today.

How the three fit together

A Zone is a broad grouping used for sales/administrative rollups — think of it as a label you attach to a set of Regions. A Region is the real geographic unit and is organised as a tree: every region (except the top-level country row) has a Parent Region, and the console tracks how deep in the tree a region sits via a computed Depth column. The conventional depth of the tree runs Country → State → District → Sub-District → Town/Village, one region_type per level. A Pincode is a postal code record that links back into that Region tree, and is where carrier serviceability and warehouse/customer addresses are ultimately anchored.

A pincode carries three links to Regions: a State, a District and a Taluka, chosen as a cascade — see the form below. That is the whole of a pincode's geography. The older single Region link, and the three India Post text boxes that sat beside the selects, were removed in August 2026; if you remember filling those in, you no longer need to.

When a customer address is created without an explicit region, it inherits the deepest of the three that is filled in — Taluka if set, otherwise District, otherwise State, otherwise nothing. Fill in as far down the cascade as you reliably can: that inherited region is what customer visibility is filtered on, so a pincode left at State level only is visible to state-wide staff but not to someone scoped to a single district.

Zones

Zones live under Geography → Zones in the sidebar. Any number of Regions can be tagged into a Zone via the Regions tab.

Zones nest. A Zone can sit under a parent Zone, so you can model a West Zone with Maharashtra and Gujarat sub-zones beneath it. Leave Parent Zone empty and the zone is top-level; set it and the zone becomes a sub-zone of whatever you choose. There is no depth limit. As with Regions, the system refuses to create a loop: you cannot make a zone its own parent, and you cannot move a zone underneath one of its own descendants — the save is rejected with an error rather than silently corrupting the tree.

Zone list

ColumnShows
nameZone name. Searchable, sortable.
codeShort unique code, shown as a Code badge. Searchable.
parent.nameLabel "Parent Zone" — the zone this one sits under, or blank for a top-level zone. Sortable.
assignedUser.u_idLabel "Assigned User" — the User ID of the staff member responsible for the zone, if one is set.
regions_countLabel "Regions" — count of Regions currently tagged to this zone. Sortable.
is_activeLabel "Active" — a tick/cross icon.
created_atDate/time created. Sortable; hidden by default — switch it on from the column toggle.

Filter: Active Status (yes/no/either) is the only filter on this list.

Screenshot placeholderThe Zones list — Name, Code badge, Assigned User, Regions count, Active icon columns, with the Active Status filter panel open.

Create / edit a Zone

Select New zone on the list, or Edit on a row. The form is a single section, "Zone Details":

FieldTypeRequiredNotes
codeTextYesMax 20, unique. Locked (disabled) once the zone exists — you cannot rename the code on edit.
nameTextYesMax 100, unique.
descriptionTextareaNoFull width.
parent_idSelect (searchable)NoLabel Parent Zone. Leave it empty for a top-level zone; set it to make this a sub-zone. Rejected if it would create a loop — see above.
assigned_user_idSelect (searchable)NoLabel "Assigned User". Options list every user as "{u_id} — {full_name}". Nullable.
is_activeToggleNoDefaults on.
Screenshot placeholderThe Zone create/edit form — Code, Name, Description, Assigned User select and the Active toggle.
Deleting a Zone is not guarded

Unlike Regions (below), Delete on a Zone has no safety check at this level — the console will let you delete a zone even if Regions are still tagged to it. Deleting simply removes the zone grouping; the Regions themselves are not deleted, but they lose their zone tag. Double-check the Regions count column before deleting a zone you are not sure is empty.

Regions tab

Open a Zone and switch to its Regions relation manager to see and manage which Regions are tagged into it.

ColumnShows
nameRegion name, searchable.
region_typeType badge (Country/State/District/SubDistrict/TownVillage) — plain styling, not colour-coded.
is_activeLabel "Active" — tick/cross icon.

Actions here: header Create (adds a brand-new region, pre-tagged to this zone), row Edit, and row Delete. Unlike the Zone delete you just read about, this Delete is guarded — it reuses the same protective check described in Deleting a Region below, even though you triggered it from inside a Zone's tab.

Regions Built

Regions live under Geography → Regions. This is the console's fullest geography editor: create, edit and delete, plus two relation managers for the region's children and its pincodes.

Region list

ColumnShows
nameRegion name. Searchable, sortable.
short_nameAbbreviated name. Searchable.
region_typeType badge — plain styling (no colour map).
zone.nameLabel "Zone" — the tagged Zone, if any.
parent.nameLabel "Parent" — the region one level up the tree.
depthHow many levels below the top of the tree this region sits. Sortable. Computed/maintained by the system — never entered by hand.
is_activeLabel "Active" — tick/cross icon.
created_atSortable; hidden by default.

Filters:

FilterNarrows to
Region TypeOne of the 5 RegionType cases: Country, State, District, Sub-District, Town/Village.
ZoneRegions tagged to a chosen Zone.
Active StatusYes / No / Either.
Parent RegionAll direct children of a chosen parent region — searchable select.
Screenshot placeholderThe Regions list with the Region Type, Zone, Active Status and Parent Region filters open, showing Name, Short Name, Type, Zone, Parent and Depth columns.

Create / edit a Region

The form is organised into three sections. Only "Region Details" is expanded by default — the rest are collapsible and start collapsed.

Region Details

FieldTypeRequiredNotes
nameTextYesMax 255.
short_nameTextNoMax 100.
report_nameTextNoMax 255 — the name used on printed reports.
region_typeSelectYesCountry / State / District / Sub-District / Town-Village.
parent_idSelect (searchable)NoLabel "Parent Region". See the cycle-prevention note just below — nullable (a Country-level region has no parent).
zone_idSelect (searchable)NoLabel "Zone". Nullable.
is_selectableToggleNoDefaults on.
is_activeToggleNoDefaults on.
You cannot make a region its own ancestor

When you edit an existing region, the Parent Region list is filtered to remove that region's own row and every one of its descendants. This is a deliberate cycle guard — without it you could pick a child of the region as its own parent and corrupt the tree. If a region you expect to see as a possible parent is missing from the list, it is almost certainly because it sits underneath the region you are editing.

Two siblings cannot share a name

A region's Name must be unique among its siblings of the same type — you cannot create two "Nagpur" districts under Maharashtra, and moving a region under a parent that already has a same-named, same-type child is refused. The rule is enforced by the database itself, so it holds for anything that creates regions, not just this form. Two same-named regions under different parents, or of different types, are fine.

Government Codes (collapsible)

FieldTypeRequiredNotes
census_codeTextNoMax 15 — this region's own-level 2011 Census code (a state row carries the state code, a district row the district code, and so on). The level is known from the region type, and each ancestor carries its own code, so there is only ever one code to type per region.
gst_state_codeTextNoMax 2 — the first two digits of a GSTIN, e.g. 27 Maharashtra, 07 Delhi. Keep the leading zero. Only appears when Region Type is "State" — see the note below.
GST State Code is a state-level fact

The GST State Code field is hidden unless Region Type is "State", and the system rejects the save if a code reaches a district, sub-district or town/village region. This is deliberate: if a village could carry its own code it could disagree with its state and quietly produce the wrong tax. For any region below state level the system looks the code up from that region's state ancestor, so there is nothing for you to fill in.

Assignments (collapsible)

FieldTypeRequiredNotes
region_account_holder_idSelect (searchable)NoLabel "Account Holder". Nullable.
region_user_idSelect (searchable)NoLabel "Region User" — the staff member responsible for this region. Nullable.
Depth and path are not form fields

The depth column you see on the list, and the underlying materialized_path the system uses for fast tree lookups, are never typed in by hand. They are derived automatically from where you place the region in the tree via Parent Region, and kept in sync by the system whenever a region moves.

Screenshot placeholderThe Region create/edit form with Region Details expanded and Government Codes and Assignments shown collapsed.

Deleting a Region

Select Delete on a region row, or the header Delete action on a region's edit page. Before anything is removed, the console runs a protective check and will refuse to delete a region that is still in use:

Delete is blocked, with a red notification, when…

…the region still has one or more active child regions underneath it in the tree, or the region is referenced by any geography scope (a user's assigned area of responsibility). In either case the delete is halted before it happens and you see a red "cannot delete" notification explaining why. To proceed, first re-parent or deactivate the region's children, and remove any geography scopes pointing at it — then delete will succeed.

This same guard is reused in two other places, so the rule is consistent everywhere you can delete a region: the Delete action inside a Region's own Children tab, and the Delete action inside a Zone's Regions tab.

Children tab

Open a Region and switch to Children to see every region one level directly beneath it.

ColumnShows
nameChild region name, searchable.
region_typeType badge.
is_activeLabel "Active" — tick/cross icon.

Header Create adds a new child directly under this region (name, type and active toggle only — the parent is set automatically). Row Edit and Delete are available; Delete uses the same guard described above.

Pincodes tab

Open a Region and switch to Pincodes to see every pincode filed under it.

ColumnShows
pincodeThe postal code, searchable.
office_namePost office name.
is_activeLabel "Active" — tick/cross icon.

This tab lists the pincodes whose State is this region. It used to follow the old single Region link; with that link gone it follows the State link instead, which is what the old one meant in practice. A district or town/village region will therefore show an empty Pincodes tab even when pincodes point at it as their District or Taluka — use Geography → Pincodes and the District filter for those.

Header Create opens a short form (pincode, office name, active toggle) that files a new pincode straight under this region. Row Edit and Delete are plain — unlike the region-delete guard above, deleting a pincode here is not blocked by any check.

Screenshot placeholderA Region's edit page with the Children and Pincodes tabs visible below the form, each showing its own mini-table and Create button.

Pincodes Built

Pincodes also have their own full page at Geography → Pincodes — the same records you can reach from a Region's Pincodes tab, but listing every pincode in the system regardless of which region it sits under, with its own filters.

Pincode list

ColumnShows
pincodeThe postal code. Searchable, sortable.
office_namePost office name. Searchable.
office_typeFree-text office type (plain text, not a badge).
stateRegion.nameLabel "State" — the linked State region, or a dash when the pincode has none.
districtRegion.nameLabel "District" — the linked District region. Hidden by default; switch it on from the column toggle.
is_activeLabel "Active" — tick/cross icon.
created_atSortable; hidden by default.

Filters: Active Status (yes/no/either), plus State and District — searchable selects over the real Region tree, matching the linked State/District described in the form below. The old Region (legacy) filter is gone, along with the link it filtered on.

Search (the box above the list) matches the pincode number and the office name only. It no longer searches place names — there is no place-name text on a pincode any more. To find every pincode in a district, use the District filter rather than typing the district's name.

A pincode with no State link is invisible to the State filter

The State and District filters work off the linked Region records, and those links are the only geography a pincode has. A pincode whose India Post name was never matched to a Region has an empty link and will simply not appear under any state or district filter — and there is no longer a text column to tell you what it should have been. That gap is exactly what the unresolved report from a bulk import is there to surface, so work that report on the day of the import.

Screenshot placeholderThe Pincodes list with the State filter open, showing Pincode, Office Name, Office Type, Region and Active columns.

Create / edit a Pincode

The form has four sections; "Pincode Details" and "System Links" are expanded, "Location" and "Contact" are collapsible.

Pincode Details

FieldTypeRequiredNotes
pincodeTextYesMax 10, unique.
office_nameTextNoMax 255.
office_typeTextNoMax 50, e.g. "Sub Office".
delivery_statusTextNoMax 50.

Location (collapsible)

Geography here is only the three linked selects, which point at real Region records. The State (India Post text), District (India Post text) and Taluka (India Post text) boxes that used to sit beside them were removed in August 2026 — the rest of the fields in this section are India Post's own postal-administration labels, which are reference text and not geography.

FieldTypeRequiredNotes
state_region_idSelect (searchable)NoLabel State — every Region of type State. This is the top of the cascade; choosing or changing it clears District and Taluka.
district_region_idSelect (searchable)NoLabel Districtdisabled until a State is chosen, then lists only that state's districts. Changing it clears Taluka.
taluka_region_idSelect (searchable)NoLabel Talukadisabled until a District is chosen, then lists only that district's talukas.
division_nameTextNoMax 150 — India Post postal division.
postal_region_nameTextNoLabel India Post Region — max 150. Not an AIR Region at all; this is India Post's own regional label. (This field was called region_name until August 2026 — it was renamed to stop exactly that confusion.)
circle_name, head_office, sub_officeTextNoMax 255 each — India Post administrative hierarchy names.
Pick State, then District, then Taluka — in that order

The three selects cascade. Each one only offers the children of the one above it, and stays greyed out until that choice is made, so you never scroll a flat list of every district in India and you cannot end up with a taluka that sits in a different state from the one you picked. If you change your mind about the State, the District and Taluka you had chosen are cleared automatically — reselect them.

The system refuses the save if the three ever disagree — a taluka must belong to the chosen district, and that district to the chosen state. The console's cascade makes that impossible to do by hand; the check exists because bulk imports and other systems write to the same records.

Go as deep as you can — District and Taluka are not optional extras

A customer address created without an explicit region inherits the deepest link this pincode has: Taluka if set, otherwise District, otherwise State, otherwise nothing at all. Customer visibility is filtered on that inherited region, so the depth you fill in decides who can see the customer. A pincode left at State level only is fine for state-wide staff but is invisible to an executive scoped to a single district — filling in the District link is what makes that customer appear on their list. Going deeper never narrows visibility: state-wide staff still see everything underneath their state.

Contact (collapsible, starts collapsed)

FieldTypeRequiredNotes
telephoneTextNoMax 50.
general_notesTextareaNoFull width.

System Links

FieldTypeRequiredNotes
ivr_dial_to_user_idSelect (searchable)NoLabel "IVR Dial To User" — which staff member's phone an inbound IVR call from this pincode's area should ring. Nullable.
is_activeToggleNoDefaults on.
Screenshot placeholderThe Pincode create/edit form showing Pincode Details and System Links expanded, Location and Contact collapsed.

Carrier Serviceability tab

Open a Pincode and switch to its relation manager to record which delivery carriers can serve this pincode.

ColumnShows
carrier_codeSearchable — the carrier's code.
is_serviceableLabel "Serviceable" — tick/cross icon.
is_metroLabel "Metro" — tick/cross icon, flags a metro-rate pincode.
zoneFree-text carrier zone code for this pincode.

Header Create opens a form: carrier_code (required, max 50), is_serviceable (toggle, defaults off), is_metro (toggle, defaults off), zone (text, max 5). Row Edit and Delete are plain, with no guard.

Bulk pincode import (CSV)

There is no upload button in the console — API only

The bulk import itself is built and working, but only as an API endpoint. No import button or file-upload dialog is wired into the console today, so if you are working from these screens alone, every pincode must still be entered one at a time through the create form above, either from Geography → Pincodes or from a Region's Pincodes tab. To load a full India Post file — tens of thousands of rows — ask a developer to run it through the API; the rest of this section describes what that run does and what you get back, because the result needs your review.

The import takes a CSV, matches each row to an existing pincode by its 6-digit code, and updates it or creates it. It runs in the background in batches, so a large file returns immediately with a job reference rather than making you wait.

Column headings must be the system's own field names, not India Post's. The recognised headings are pincode (required) plus office_name, office_type, delivery_status, division_name, postal_region_name, circle_name, head_office, sub_office, telephone, general_notes, is_active, the three place-name columns state_name, district_name, taluka, and the three link columns state_region_id, district_region_id, taluka_region_id. Anything else in the file is ignored.

The place-name columns are still read from the file even though a pincode no longer stores them — they are the input to the matching described below, and nothing else. An India Post export works unchanged; the names are used to find the Regions and are then discarded. The old region_id heading is no longer recognised.

The postal-region heading changed to postal_region_name

If you are reusing an import file prepared before August 2026, its postal-region column is probably headed region_name. That heading is no longer recognised and the column will be silently ignored — no error, the data simply does not arrive. Rename it to postal_region_name before importing.

What the import does with State, District and Taluka

Normally you put only the names in the file. The import reads the state_name, district_name and taluka text and tries to match each one to a real Region record, then sets the links for you. Matching ignores capitals and stray spaces, so NAGPUR, Nagpur and Nagpur  all land on the one Nagpur record. It also ignores a leading "The" in either direction — India Post writes THE DADRA AND NAGAR HAVELI AND DAMAN AND DIU where our record has no "The" (52 post offices used to fail on that alone), while The Nilgiris is a genuine district name, so a file saying Nilgiris matches it too. That is the whole point of the exercise: one row per real place, no near-duplicate spellings.

You can also supply the links directly

If you have gone to the trouble of preparing a curated file, put the Region's id in state_region_id, district_region_id or taluka_region_id and the import will use it as-is and skip name matching for that level — the id wins, the name is not even consulted. Mix and match freely: an id for the state and a name for the district works fine. The usual safety checks still run on every id, so a wrong one (not a real region, wrong level, or a taluka that does not sit in the district you gave) fails that one row and leaves the rest of the file alone. Use this when the names are too messy to match; otherwise let the names do the work.

Two rules govern what happens when a name does not match:

Reviewing the "unresolved" report

Checking the import's progress also returns the list of names it could not match. Two things come back:

FieldShows
unresolved_countThe true total of unmatched names across the whole file.
unresolvedA review list — for each unmatched name: which level it was (state, district or taluka), the raw_name exactly as it appeared in the file, and the pincode of the row it came from. Capped at 500 entries, so on a badly-mismatched file the list is a sample and unresolved_count is the real number.

A non-zero count is not a broken import — the pincodes themselves were created or updated normally. It means those rows are missing their geography links, so they will not show up under the State or District filters and will not be counted in any report that goes by region. Work through the list: usually the fix is to create the missing District or Taluka Region (under the correct parent), then re-run the import so the names match on the second pass.

The unresolved report expires after 24 hours

It is held in temporary storage, not saved against the import as a permanent record. Read it, or export it, on the day of the import — after that it is gone and the only way to find the gaps again is to re-run the file.

A last prerequisite worth knowing: state matching only works because all 28 states and 8 union territories are pre-loaded as Regions when the system is set up. Districts and talukas are not — they arrive from imports and from the old-system migration, which is why they are the levels that usually turn up in the unresolved list. On the full India Post file that split is measured: 99.47% of pincodes match a state (19,483 of 19,587; the handful that do not are rows where India Post wrote the state as literally NA), and none match a district, simply because no districts exist yet to match against. Expect a very long unresolved list on the first run — that is the districts, and it is the work list for creating them.

Where else these records are used

Regions and Pincodes are not self-contained — other modules point at them:

See also Warehouses & Carriers for how Regions attach to a Warehouse's serviceable area, and Schedulers for the background jobs that read warehouse/region data every day.