NetSuite beforeSubmit vs afterSubmit: Choosing the Right User Event Trigger
Need help applying this in your account?
Quick answer
In NetSuite User Event scripts, beforeSubmit fires after the user clicks Save but before the record is written to the database. The record exists in memory and can still be modified or the save can be blocked with an error. afterSubmit fires after the record is committed to the database and has a permanent internal ID. Use beforeSubmit to validate data, set computed field values, or block invalid saves. Use afterSubmit to create related records, send notifications, or trigger follow-on actions that require the saved record to already exist. The most common mistake is using afterSubmit to modify the current record, which requires an additional record.load() and record.save(), triggering another round of User Event scripts on the same record. This pattern can cause unexpected recursive script execution and consumes significantly more governance units than doing the same modification in beforeSubmit.
What Is the Difference Between beforeSubmit and afterSubmit?
NetSuite User Event scripts fire in response to record saves. Both beforeSubmit and afterSubmit run when a record is saved, but they fire at different points in the save process, and that difference determines what each one can and cannot do.
beforeSubmit fires after the user clicks Save but before the record is written to the database. The record exists in memory but is not yet committed.
afterSubmit fires after the record has been successfully written to the database. The record now has a permanent ID and all its data is committed.
Getting this wrong is one of the most common sources of subtle SuiteScript bugs, logic that looks correct but fails silently, or runs at the wrong time.
What Can beforeSubmit Do?
Because beforeSubmit fires before the record is committed, it has two capabilities that afterSubmit does not:
1. Modify the current record
You can use context.newRecord.setValue() to change field values before they are saved. The modified values are what gets committed to the database.
function beforeSubmit(context) {
if (context.type === context.UserEventType.CREATE) {
var total = context.newRecord.getValue('amount');
context.newRecord.setValue({
fieldId: 'custbody_approved_amount',
value: total
});
}
}2. Abort the save
If you throw an error in beforeSubmit, the save is cancelled. The record is not committed, and the user sees the error message. This makes beforeSubmit the right place for validation logic.
function beforeSubmit(context) {
var status = context.newRecord.getValue('approvalstatus');
var amount = context.newRecord.getValue('amount');
if (status === 'Approved' && amount > 100000) {
throw new Error('Orders over $100,000 require director approval before final approval.');
}
}What Can afterSubmit Do?
afterSubmit runs after the record is safely committed. The record has a permanent ID (context.newRecord.id) and is available in the database.
This is the right place for actions that should only happen after a successful save:
- Creating or updating related records
- Sending notifications or emails
- Making calls to external systems or APIs
- Triggering follow-up workflows
function afterSubmit(context) {
if (context.type === context.UserEventType.APPROVE) {
var orderId = context.newRecord.id;
// Create a fulfillment record, send a notification, etc.
// Safe here because the order is already committed
}
}What afterSubmit cannot do: You cannot modify the current record's field values using setValue() on context.newRecord. The record is already saved, your changes won't persist. If you need to update the record after save, you would need to use record.submitFields() with the record's ID, which triggers another save cycle (use with care to avoid loops).
What Is the Most Common Mistake with These Triggers?
Placing external API calls or record creation logic in beforeSubmit.
If the API call fails or takes too long, it can cause the record save to fail or time out, even when the record data itself is valid. External calls belong in afterSubmit, where the record is already committed and a failure in the external system does not roll back the user's save.
The pattern to avoid:
// Wrong placement: external call blocks the save
function beforeSubmit(context) {
// If this API call fails, the record save fails too
callExternalApi(context.newRecord.getValue('entity'));
}The correct pattern:
// afterSubmit: external call runs after the record is safe
function afterSubmit(context) {
// Record is committed; an API failure won't affect the save
callExternalApi(context.newRecord.getValue('entity'));
}Quick reference
| beforeSubmit | afterSubmit | |
|---|---|---|
| Record committed? | No | Yes |
| Modify current record? | Yes | No |
| Abort the save? | Yes (throw error) | No |
| Record ID available? | Only on edit/approve | Yes (create and edit) |
| External API calls? | Avoid | Yes |
| Create related records? | Avoid | Yes |
| Validation logic? | Yes | No |
Which Trigger Should You Use?
If the logic must modify the current record or can abort the save, it belongs in beforeSubmit.
If the logic should only run after the record is committed, notifications, related record creation, external system calls, it belongs in afterSubmit.
When in doubt, prefer afterSubmit. It runs after the user's data is safe, so a bug in your script cannot accidentally cancel a legitimate save.
Related Resources
How to Migrate NetSuite RESTlet Integrations from NLAuth to Token-Based Authentication
NLAuth stops working in NetSuite 2027.1. Here are the steps to audit your RESTlet integrations and migrate them to Token-Based Authentication before the deadline.
How to Use Sequential Batch Processing in NetSuite REST Web Services
NetSuite 2026.2 adds sequential processing for REST batch operations so you can run multiple API requests in a guaranteed order. Here is when to use it and what it means for dependent operations.
Need help applying this in your account?
We work with post-go-live NetSuite accounts every day. Tell us what you're working on.