Ch. 17 · ServiceNow

ServiceNow Record Producers

How a record producer turns catalog variables into one record on a target table, what its script may do, and how to handle related records.

~6 min readintermediateupdated Oct 19, 2026

A record producer is a catalog item that, instead of ordering something, creates a record. An employee opens “Report a laptop problem” on the portal, answers five friendly questions, and an incident appears on the incident table with the category, description and urgency filled in. The same pattern covers HR inquiries, facilities requests, security reports and anything else where you want a guided form in front of a table that’s awkward for end users.

Interviewers ask about record producers because they sit between two worlds. They look like catalog items (variables, catalog client scripts, portal pages) but they produce ordinary task records, not requests. Knowing which rules come from which side is most of the topic.

What a producer creates, and what it doesn’t

When someone submits a normal catalog item, the platform creates a request (sc_request), a requested item (sc_req_item) and whatever catalog tasks the fulfilment flow adds. A record producer skips all of that. It creates exactly one record, on the table named in its Table name field, and that’s it. There’s no RITM, no request number, no catalog fulfilment flow.

The variable answers still exist. They’re stored against the created record, so an agent opening the incident can see what the employee typed in the variable editor on the form. That’s useful for questions that don’t map to any field, like “When did you last restart?”.

Because the output is just a record, everything else that applies to the table applies to it: business rules, assignment rules, SLAs, notifications and the table’s flows all run on the insert. That’s the main reason to choose a producer over a catalog item when the work is really an incident or a case: the existing process picks it up with no extra wiring.

Three ways to fill fields

A producer can set fields on the new record in three places, and you’ll see all three on the producer form.

Template. Static assignments, for values that never depend on the answers. Category “hardware” for a laptop form is a good example.

Variable mapping. Each variable has a “Map to field” option. Tick it, pick the field, and the answer goes straight into that field. This is the cleanest option when one question equals one field.

Script. Runs on the server before the record is inserted. It has two objects: current, the record that’s about to be created, and producer, which exposes the variable answers by name (producer.symptom). Use it for values that are derived, combined or conditional.

Newer releases also have a Post insert script, which runs after the record exists. ServiceNow’s documentation notes that its assignments override both the target record values and the template values, so treat it as the last word.

Here’s the laptop producer, written as it would be on an instance. It’s illustrative; no instance was available while writing it, so check variable and field names against yours.

// Record producer script: "Report a laptop problem" (target table: incident)
current.short_description = 'Laptop issue: ' + producer.symptom;
current.contact_type = 'self-service';
if (producer.blocking == 'yes') {
  current.urgency = 1;   // the employee cannot work
}
// No current.insert() here: the platform inserts the record after this script finishes.
JavaScript

The duplicate record bug

The single most common record producer bug in code reviews is a script that ends with current.insert(). It looks reasonable if you’re used to GlideRecord, but the platform performs the insert itself once the script returns. Call it yourself and you get two records.

I modelled the submission pipeline in Node to show the effect: apply the template, apply mapped variables, run the script, then insert once. The second producer is identical except that its script calls current.insert():

const buggy = { ...laptopIssue, script(current, producer) { laptopIssue.script(current, producer); current.insert(); } };
JavaScript
┌─────────┬────────┬─────────────────────────┬─────────────┬─────────┐
│ (index) │ sys_id │ short_description       │ subcategory │ urgency │
├─────────┼────────┼─────────────────────────┼─────────────┼─────────┤
│ 0       │ 'rec1' │ 'Laptop issue: flicker' │ 'laptop'    │ '1'     │
│ 1       │ 'rec2' │ 'Laptop issue: fan'     │ 'laptop'    │ '3'     │
│ 2       │ 'rec3' │ 'Laptop issue: fan'     │ 'laptop'    │ '3'     │
└─────────┴────────┴─────────────────────────┴─────────────┴─────────┘
Text

The first submission produced one incident with urgency raised to 1 because the employee said they were blocked. The buggy one produced two identical incidents, rec2 and rec3. On a real instance the second copy also gets its own number, SLA and notification, so the employee receives two emails and the service desk works the ticket twice.

The model also shows why it pays to set each field in exactly one place. If the template sets urgency to 3 and the script sets it to 1, the result depends on the order of evaluation, and whoever maintains the producer next has to know that order. Keep static values in the template, one-to-one answers in mappings, and only derived values in the script.

When the request touches more than one table

The limitation people run into is that the target is a single table. Some real forms want more: a new-starter form that should create an HR case plus a hardware request, or a security report that should create an incident and a linked security incident.

You can’t change the target table from the script, and you shouldn’t try to build several records inside the pre-insert script, because the main record doesn’t exist yet and there’s nothing reliable to link to. Better options, in rough order of preference:

  • Use a flow triggered on the created record. The producer creates the HR case; a flow on HR case creation, filtered to records from this producer, creates the hardware request and links it. The logic is visible, testable and retried properly.
  • Use the post insert script for small follow-ups that need the new record’s sys_id, such as creating one related record.
  • Use an order guide when the user is really ordering several catalog items, rather than reporting one thing.
  • Split the producer when the answers decide which table is right, for example separate “IT problem” and “HR question” producers rather than one form that tries to route to either.

Client-side behaviour lives in catalog scripts

The form the employee sees is a catalog form, so it uses catalog client scripts and catalog UI policies attached to the producer or its variable sets, not the incident table’s client scripts. g_form works on variables by name (g_form.setMandatory('symptom', true)), and the scripts run on the Service Portal and Employee Center as long as you avoid synchronous calls and DOM access. Remember that the target table’s form scripts don’t run during submission, and the catalog scripts don’t run when an agent later edits the incident. Validation that has to hold for both belongs on the server, in a before business rule or data policy on the target table.

Interview tip

Put shared questions like “Who is this for?” in a variable set and reuse it across producers. Then the client logic and the mapping for those questions are defined once.

Exercise: why did every laptop report arrive twice?

After a producer change, the service desk sees every “Report a laptop problem” submission as two identical incidents. Nothing else on the incident table changed. What do you check first, and how do you fix it without losing the change the developer wanted?

Answer

First, open the producer’s script and look for current.insert() or current.update(). The platform inserts the record after the script, so an explicit insert creates the first copy and the platform creates the second. That matches “two identical incidents only from this producer”. I’d confirm by checking the two records’ created timestamps (milliseconds apart) and the producer’s update history. The fix is to remove the insert call; anything the developer added it for usually falls into one of two cases. If they needed values set, those assignments already work without the insert. If they needed the new sys_id to create a related record, that work moves to the post insert script or to a flow triggered on the new incident. Then I’d clean up the duplicates already created, ideally by closing them as duplicates rather than deleting, and add an ATF test that submits the producer and asserts that exactly one incident exists.

More in ServiceNow

read ✓ServiceNow · hard

ServiceNow ACL Scripting and Access

When an ACL really needs a script, how to write one that is cheap and null-safe on every row, and how to prove it works by testing as the user.

~7 min readread →
read ✓ServiceNow · hard

ServiceNow Caching and Invalidation

What a ServiceNow instance caches on each node, why a system property change triggers a costly flush, and where changing settings belong.

~7 min readread →
read ✓ServiceNow · mid

ServiceNow Dictionary and Field Audit

How dictionary entries and per-table overrides define a field, how auditing writes to sys_audit, and which fields you should keep out of history.

~6 min readread →
esc