Learn how to design and configure Dataverse auto-numbering columns that generate reliable, auditable record identifiers for procurement, compliance, and business process workflows. Covers format string patterns, seed management, API configuration, and production deployment strategies.

Imagine you've just deployed a contract management app for a mid-sized procurement team. Users are creating contracts daily, and your compliance officer needs to reference records in audit trails, vendor correspondence, and approval workflows. Everyone starts asking: "What's the contract number?" And then the chaos begins — some users type their own identifiers into a plain text field, others leave it blank, and two records end up with the same number because two people hit save at the same time. Sound familiar?
This is exactly the problem Dataverse auto-numbering columns solve. Rather than relying on users to generate meaningful identifiers — or worse, relying on the GUID that Dataverse assigns internally — you can configure a system-managed column that follows a predictable pattern, increments reliably without duplicates, and becomes the human-readable backbone of your auditable business process. Done right, an auto-number column becomes the thread that connects a record across emails, reports, printed forms, and regulatory submissions.
By the end of this lesson, you'll know how to design, configure, and maintain auto-numbering columns that actually hold up in production. We'll cover the anatomy of sequence patterns, how to set seed values and reset strategies, how to surface auto-numbers correctly in forms and views, and how to handle the edge cases that bite teams after go-live.
What you'll learn:
You should be comfortable with Dataverse table and column configuration at the level covered in Dataverse Fundamentals: Tables, Columns, and Rows Explained for Power Apps Makers. You should also understand how model-driven app forms display column data — if that's rusty, review Designing Model-Driven Forms: Sections, Tabs, Subgrids, and Quick View Forms before continuing. Familiarity with solutions and publisher prefixes is assumed; we'll touch on them in the context of deployment.
Before you configure anything, it's worth understanding what Dataverse is doing on your behalf. Auto-number columns are a special subtype of the Text column type. When a record is saved for the first time, Dataverse's server-side engine evaluates the column's format string, resolves any date tokens, looks up the current sequence counter for that column, increments it atomically, and writes the result into the column — all within the same database transaction that creates the record.
The critical word there is atomically. The counter increment happens inside a lock, which means two simultaneous saves cannot receive the same number. This is fundamentally different from trying to calculate the "next number" using a rollup column or a Power Automate flow — both of which have race conditions that will eventually produce duplicates under load.
Key insight
Auto-number columns are populated server-side at record creation. This means they are never populated in a quick-create dialog preview, they cannot be set by users, and they will not appear until after the first save. Design your forms accordingly.
The column stores its value as a plain string, which gives you tremendous formatting flexibility. The sequence counter itself lives in Dataverse's internal metadata and is scoped to the specific column definition — so two different tables can both have an auto-number column counting from 1 without interfering with each other.
One important constraint: auto-number columns are read-only after creation. Once the value is written, Dataverse will not let a user or a standard update operation overwrite it. You can change the value programmatically with elevated API calls, but that's an administrative action, not a routine one. This immutability is what makes them suitable for audit identifiers.
Resist the urge to jump straight into the maker portal. The pattern you choose becomes permanent in the sense that changing it later means all existing records have old-format numbers and new records have new-format numbers — a nightmare for searchability and compliance. Spend ten minutes designing before you build.
Dataverse auto-number format strings support three types of tokens, which you combine with literal characters:
| Token type | Syntax | Example | Result |
|---|---|---|---|
| Sequence number | {SEQNUM:N} |
{SEQNUM:5} |
00042 |
| Date/time | {DATETIMEUTC:format} |
{DATETIMEUTC:yyyy} |
2025 |
| Random string | {RANDSTRING:N} |
{RANDSTRING:4} |
K7X2 |
You combine these with literal characters — letters, numbers, hyphens, slashes, anything that's valid in a string. Here are realistic patterns for common business scenarios:
Procurement contract:
CTR-{DATETIMEUTC:yyyy}-{SEQNUM:5}
Produces: CTR-2025-00001, CTR-2025-00002
Support ticket:
TKT-{SEQNUM:6}
Produces: TKT-000001, TKT-000042
Invoice with region prefix:
INV-EMEA-{DATETIMEUTC:yyyyMM}-{SEQNUM:4}
Produces: INV-EMEA-202503-0017
Audit finding with random suffix (for external sharing where sequential exposure is undesirable):
AUD-{SEQNUM:5}-{RANDSTRING:3}
Produces: AUD-00008-M4Q
Sequence width: Choose your padding based on expected volume over the lifetime of the system, not just this year. If you're numbering support tickets for a team that closes 500 a month and you expect to run for five years, you'll hit 30,000 tickets — {SEQNUM:5} (max 99,999) is fine. But if volume might reach 100,000+, go to {SEQNUM:6} now. Changing later is painful.
Date tokens in the pattern: Including a year in the pattern ({DATETIMEUTC:yyyy}) creates the impression that sequence numbers reset annually — but they do not unless you explicitly reset the seed. A record created in January 2025 might be CTR-2025-00041, and a record created in January 2026 will be CTR-2026-00042. The year is cosmetic context, not a reset boundary. If annual resets matter for your business process (many procurement departments require this), you'll need to handle that with a scheduled Power Automate flow that resets the seed — we'll cover that later.
Random strings: {RANDSTRING:N} generates a random alphanumeric suffix. This is useful when you're sharing identifiers externally and don't want recipients to infer your record volume or iterate through identifiers. The tradeoff is that records become harder to sort meaningfully. For internal audit workflows, pure sequential numbers are usually preferable.
Warning
Do not include spaces in your format string. While they're technically allowed, they cause consistent problems with URL encoding, Excel imports, and certain lookup searches. Use hyphens or underscores as separators instead.
With your pattern designed, let's build it. We'll use the Contracts table from our procurement scenario.
Navigate to make.powerapps.com, select your environment, and open Tables from the left navigation. Find your table (for this exercise, Contract) and open it.
In the Columns tab, select New column. The column creation panel opens on the right.
Contract Numbercr8a2_contractnumber (your publisher prefix will vary). Accept the default.Once you select Autonumber, the panel expands to show the auto-number configuration:
{SEQNUM} token — set it to 5 for our contract scenario1000. Change this to 1 unless you have a specific reason to start higher.Wait — where's the format string input? This is a common point of confusion. In the current maker portal UI, the format string itself is not exposed through the GUI for custom patterns. The portal only gives you the seed and digit count, and it generates a default pattern like {SEQNUM:5}.
To set a custom format string with prefixes and date tokens, you must use the API or PowerShell. Don't let this slow you down — the API call is straightforward and we'll walk through it next. Go ahead and create the column with the default settings now; you'll update the format string immediately after.
Tip
Even if your pattern doesn't need a date token, give your column a meaningful display name and a description that documents the pattern. Future administrators (including future you) will thank you. Column descriptions are visible in the column detail panel in the maker portal.
To set a custom format string, you'll send a PATCH request to the Dataverse metadata endpoint. You can do this with Postman, the browser's developer tools against the environment URL, or — most conveniently — using the built-in tools in Power Platform CLI or a simple HTTP request from Power Automate.
Here's the API call pattern:
Endpoint:
PATCH https://yourorg.crm.dynamics.com/api/data/v9.2/EntityDefinitions(LogicalName='cr8a2_contract')/Attributes(LogicalName='cr8a2_contractnumber')
Headers:
OData-MaxVersion: 4.0
OData-Version: 4.0
Content-Type: application/json
Accept: application/json
Request body:
{
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
"AutoNumberFormat": "CTR-{DATETIMEUTC:yyyy}-{SEQNUM:5}"
}
Replace yourorg with your actual environment subdomain, cr8a2_contract with your table's logical name, and cr8a2_contractnumber with your column's logical name.
If you're using the Power Platform CLI, you can script this as part of your solution deployment. If you're doing this interactively in Postman, authenticate with OAuth 2.0 against your tenant.
After the PATCH succeeds (you'll get a 204 No Content response), go back to the maker portal and verify the column. You won't see the format string displayed there, but you can confirm it by creating a test record and checking that the generated value matches your pattern.
Create a test record in your Contract table — use the app or the table's Edit in table designer quick data entry. After saving, the Contract Number field should display CTR-2025-00001 (assuming you're in 2025 and this is your first record).
If the field shows as blank or shows the default 1000-based format instead of your custom pattern, double-check the PATCH request. Common issues include using the display name instead of the logical name, or targeting the wrong entity in the URL.
For teams working in a proper ALM pipeline, configuring auto-number patterns through the API in an ad-hoc manner is fragile — the setting lives in environment metadata and doesn't automatically travel with your managed solution. The better approach is to script the configuration and run it as part of your deployment pipeline.
Install the Power Platform CLI:
pac auth create --url https://yourorg.crm.dynamics.com
Then use a PowerShell script to set the format:
$environmentUrl = "https://yourorg.crm.dynamics.com"
$tableName = "cr8a2_contract"
$columnName = "cr8a2_contractnumber"
$formatString = "CTR-{DATETIMEUTC:yyyy}-{SEQNUM:5}"
$headers = @{
"OData-MaxVersion" = "4.0"
"OData-Version" = "4.0"
"Content-Type" = "application/json"
"Accept" = "application/json"
}
$body = @{
"@odata.type" = "Microsoft.Dynamics.CRM.StringAttributeMetadata"
"AutoNumberFormat" = $formatString
} | ConvertTo-Json
$uri = "$environmentUrl/api/data/v9.2/EntityDefinitions(LogicalName='$tableName')/Attributes(LogicalName='$columnName')"
Invoke-RestMethod -Method PATCH -Uri $uri -Headers $headers -Body $body `
-UseDefaultCredentials
Store this script in your solution repository and include it in your environment provisioning runbook. This ensures that when your managed solution is imported into UAT or production, the format string gets applied consistently.
Note
Auto-number format strings are stored in environment metadata, not in the solution XML. This is a known gap in the Power Platform solution framework. If you import a solution that contains an auto-number column into a new environment, the sequence counter starts fresh and the format string reverts to the default unless you run your configuration script separately.
The seed value controls where the sequence counter starts. This is more important than it sounds.
The default seed of 1000 means your first record gets CTR-2025-01000. If your business prefers records to start at 1, set the seed to 1 when you create the column. If you're migrating from a legacy system that already has records numbered 1 through 847, set the seed to 848 so new records don't collide with migrated data.
To reset the seed after the column already exists, you need to call the SetAutoNumberSeed action:
POST https://yourorg.crm.dynamics.com/api/data/v9.2/SetAutoNumberSeed
Request body:
{
"EntityName": "cr8a2_contract",
"AttributeName": "cr8a2_contractnumber",
"Value": 1
}
This sets the next sequence value. After this call, the next record created will receive sequence number 1 (or 00001 with your padding). Existing records are unaffected.
Warning
Resetting the seed to a value lower than the current counter will immediately cause duplicate number generation if records exist that were numbered above your reset point. Before resetting, always verify whether any records with the higher numbers exist, or use the reset only when starting fresh in a new period where the date component prevents collision.
For procurement teams that require contract numbers like CTR-2025-00001 through CTR-2025-NNNNN and then restart at CTR-2026-00001 on January 1st, you need to reset the seed at the turn of the year. Build a scheduled Power Automate cloud flow that runs on January 1st at midnight UTC and calls the SetAutoNumberSeed action with value 1. Schedule this flow as part of your operational runbook, and document it — it's the kind of annual maintenance task that gets forgotten and then causes an incident.
An auto-number column is only valuable if users can find it quickly. Let's talk about placement and behavior in the model-driven app interface.
Because the auto-number column is blank until after the first save, placing it prominently in the form header is the right call for most scenarios. The form header area (the top section that shows the record name alongside key fields) persists as users scroll, making the identifier always visible once it's populated.
To add the Contract Number to the form header:
Open your form in the form designer, select the header area, and drag your Contract Number column into one of the header column slots. Set it to Read-only on the form (it's system-read-only anyway, but making it explicit prevents confusion). If your form header already shows the record name field, Contract Number can sit alongside it.
On the main form body, consider adding the field to the first section of the first tab in a read-only mode with a clear label. Users frequently scan the top of a form when they need to quote a reference number in an email.
Tip
Add the auto-number column to your Quick View form if this table is looked up from other tables. When a user selects a contract in a lookup field from, say, the Invoices table, the quick view popup should show the contract number prominently so they can confirm they've selected the right record. See Configuring Model-Driven App Quick Forms and Card Forms: Displaying Related Record Summaries in Lookups and Subgrids for configuration details.
When a user opens a new (unsaved) record form, the auto-number field will show as blank or show placeholder text. This is correct behavior — the number doesn't exist yet. However, users sometimes interpret this as an error. Mitigate this with a business rule that sets the field label to display as a helper message when the record is new — though note that business rules have limited ability to modify read-only system fields. A more practical solution is a field description set to "Auto-assigned on save."
Add the Contract Number column as the first or second column in your default Active Contracts view. It should be sortable (it sorts lexicographically, so consistent padding matters — another reason {SEQNUM:5} beats {SEQNUM}) and it should be wide enough to display the full pattern without truncation.
In your Quick Find view, add Contract Number to the find by columns. This lets users search for CTR-2025-00042 directly from the search bar and land on the right record instantly. Learn more about configuring find columns in Configuring Model-Driven App Views as Default Views, Quick Find Views, and Lookup Views.
When Contract records appear in lookup dialogs from other tables (invoices, amendments, disputes), users need to identify the right contract. Make sure the Lookup view for your Contract table includes the Contract Number column and ideally the Vendor name. Without it, users see a list of undifferentiated contract names and inevitably select the wrong one.
Auto-number columns integrate naturally with Business Process Flows because the number is generated at record creation — which typically corresponds to the first stage of the process. By the time users reach stage two (say, "Legal Review"), the contract number is already established and can be communicated to stakeholders.
In approval workflows built in Power Automate, always include the auto-number value in notification emails and Teams messages. It becomes the natural reference point across all communications about that record: "Please review Contract CTR-2025-00042 before Thursday."
For audit trail purposes, pair your auto-number column with Dataverse's built-in auditing. Because the auto-number column is written at creation and never changed, it appears in the audit history as a creation event. This gives you a permanent, system-generated record of when each identifier was assigned. Review Configuring Dataverse Auditing and Field-Level Change History in Model-Driven Apps to ensure auditing is enabled for your table and that the auto-number column is included in the audited columns list.
Key insight
Auto-number columns are perfect audit anchors because they combine three properties: they're system-generated (no user can forge them), they're immutable after creation (no user can change them), and they're human-readable (they appear in emails, reports, and verbal references). Combine them with field-level auditing and you have a reliable chain of custody for each record's identity.
Let's build the complete auto-numbering setup for a procurement contract management scenario. This exercise assumes you have a Contract table with basic fields (Vendor, Value, Status, Owner) already in place.
Contract Number auto-number column with a year-prefixed sequential patternIn your Contract table, add a new column:
Contract NumberSave the column. You'll now have a column with the default format {SEQNUM:5}. We'll update this next.
Use the following PATCH call (adapt to your org URL and column logical name):
{
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
"AutoNumberFormat": "CTR-{DATETIMEUTC:yyyy}-{SEQNUM:5}"
}
Confirm the column logical name from the maker portal — it will be something like new_contractnumber or cr8a2_contractnumber depending on your publisher prefix.
Open the main Contract form. Drag Contract Number into the form header. In the field properties, set it to Read-only. Add a second instance of the field in the Summary tab's first section (also Read-only) so it's visible without scrolling on long forms.
Open the Active Contracts default view. Add Contract Number as the first column, with a width of 180px. Sort the view by Contract Number ascending as the default sort.
Open the Quick Find Active Contracts view. In the Find By columns, add Contract Number. Save and publish.
Create three contract records in your app. After saving each one, verify that:
CTR-2025-00001, CTR-2025-00002, and CTR-2025-00003CTR-2025-00002 in the search bar returns the correct recordIf any record shows a blank or default-format number, check that your PATCH call succeeded and that you're looking at the correct table and column.
Cause: The most common reason is that the auto-number column wasn't included in the form before testing — you're seeing a field that wasn't populated because it was never configured, not because it failed to generate. Check the actual column value in the table designer's data view.
Fix: Verify the value in the table's data view (Tables > Your Table > Edit). If the value is there but not showing on the form, add the column to the form and republish.
Cause: The PATCH request targeted the wrong entity or attribute logical name, or it returned an error you didn't notice.
Fix: Re-run the PATCH request and watch for the response status code. A 204 means success. A 400 or 404 means your URL is wrong. Log the response body on error — it will tell you exactly what failed.
Cause: This should not happen with Dataverse's native auto-numbering. If it does, investigate whether someone ran a bulk import that bypassed the auto-number mechanism, or whether the seed was reset improperly.
Fix: Check for records imported via Excel or Dataflow that had values set explicitly in the auto-number column. When importing data, the import tool will honor an explicit value in an auto-number column if you map it. Importing data via Dataflows requires careful handling — map the auto-number column only if you're migrating legacy identifiers intentionally. If you're loading new records through an import, leave the auto-number column unmapped so the system generates values.
Cause: Records were created and then deleted. Dataverse does not decrement the counter when records are deleted. If you create records 1 through 50 and then delete 40 of them, the next record will still be numbered 51.
Fix: This is by design. Gaps in auto-number sequences are normal and expected in any production system. If you need a gapless sequence for regulatory reasons (some financial regulations require this), auto-numbering alone isn't sufficient — you'd need a custom server-side plugin that enforces gaplessness, which is outside the scope of this lesson.
Cause: As noted earlier, auto-number format strings live in environment metadata, not solution XML.
Fix: Include the PowerShell configuration script in your deployment pipeline and run it immediately after solution import in each environment. Document this in your team's deployment checklist.
Warning
Never reset the seed in your production environment without a change control record and a backup of the current sequence state. A mistaken seed reset that causes duplicate numbers can undermine the integrity of your entire audit trail, potentially invalidating compliance reports.
Cause: The read-only enforcement on auto-number columns applies correctly in the Unified Interface (the modern model-driven app shell), but some organizations still have access to legacy endpoints.
Fix: Ensure your app is published as a Unified Interface app, and disable the legacy web client access if your organization policy allows. Also confirm that the field's form-level read-only setting is configured, which adds a second layer of protection independent of the system enforcement.
In organizations with development, UAT, and production environments, you often want auto-number patterns that make it obvious which environment a record originated from. This is especially useful when test data leaks into discussions about production records.
Configure your patterns as:
CTR-DEV-{SEQNUM:5}CTR-UAT-{SEQNUM:5}CTR-{DATETIMEUTC:yyyy}-{SEQNUM:5}Your deployment script applies the right format string based on the target environment URL. This way, if a record from the UAT environment ever surfaces in a production conversation, the UAT prefix makes it immediately obvious.
This pattern also protects you during data validation exercises where business stakeholders review UAT data to confirm app behavior — they can't accidentally approve a UAT contract thinking it's real.
Auto-numbering columns are one of those features that seem simple on the surface but have meaningful depth when you're designing for production business processes. The key takeaways:
From here, consider exploring how Configuring Dataverse Auditing and Field-Level Change History in Model-Driven Apps can pair with auto-numbering to create a complete audit story, and how Configuring Dataverse Column-Level Business Rules and Multi-Condition Logic can enforce surrounding field requirements that complement your identifier strategy — like requiring a vendor assignment before a contract number is issued.
Auto-number columns are the kind of infrastructure investment that your team stops thinking about after the first week because they just work. That invisibility is the goal: a reliable, system-managed identifier that underpins every process without requiring any ongoing human attention.
Model-Driven Apps & Dataverse
Configuring Dataverse Table Queues and Routing Rules in Model-Driven Apps: Managing Work Assignment, Queue Item Lifecycles, and Team-Based Record Distribution
Configuring Dataverse Connection Roles and Relationship Categories: Modeling Party-to-Party Associations Between Records in Model-Driven Apps