Debugging NetSuite RESTlet Auth Errors: INVALID_LOGIN_ATTEMPT, TBA, and OAuth
Debugging NetSuite RESTlet Auth Errors: INVALID_LOGIN_ATTEMPT, TBA, and OAuth
NetSuite authentication errors tend to show up as nothing. The calling system gets an empty response. NetSuite's execution log has no entry, because authentication failed before the script ran.
The errors covered here are INVALID_LOGIN_ATTEMPT (TBA calls), invalid_grant (OAuth 2.0), missing role permissions, and RESTlet scope problems. Related: the TBA to OAuth 2.0 migration guide, the silent failure diagnosis post, and the wider NetSuite integration guide.
Which auth method are you actually using?
RESTlets (script type restlet, URL format /app/site/hosting/restlet.nl) accept two authentication methods for external calls. Token-Based Authentication: a consumer key, consumer secret, token ID, and token secret signed per-request using HMAC-SHA256 (OAuth 1.0a). Or OAuth 2.0: an Authorization: Bearer header carrying an access token issued for the RESTlets scope.
REST Web Services (/services/rest/record/v1/...) use OAuth 2.0 bearer tokens. TBA still works here today, but from 2027.1 new integrations cannot be created with it.
The error string tells you which layer failed. INVALID_LOGIN_ATTEMPT comes from a TBA-signed request that failed authentication. invalid_grant comes from NetSuite's OAuth 2.0 token endpoint, whichever surface the token was for.
INVALID_LOGIN_ATTEMPT
This error appears in the HTTP response body when a RESTlet call fails at the authentication layer. The script never runs; the execution log has no entry for the call.
Cause 1: Token pair revoked
Go to Setup > Users/Roles > Access Tokens. Find the token associated with your integration. If it is not there, it has been deleted. Common triggers:
- A user's role was changed or removed. Access tokens are tied to a user-role pair; when the role association changes, the token is invalidated.
- An administrator rotated credentials manually.
- The Integration Record was edited or re-saved, which can invalidate existing tokens in some account configurations.
Generate a new token in the Access Tokens list using the same Integration Record and role. Update the credentials in your calling system.
Cause 2: Wrong consumer key or secret
Integration credentials are shown once when the Integration Record is saved. If those values were not stored correctly, the consumer key or secret in your calling system is wrong.
Go to Setup > Integration > Manage Integrations and open the relevant Integration Record. You cannot view the existing credentials, but you can re-save the record to generate new ones. Update your calling system with the new values.
Cause 3: IP address restriction
Integration Records have an optional "Allowed IPs" field. If your calling server's IP is not in the list, authentication fails with INVALID_LOGIN_ATTEMPT even if the credentials are correct.
Check the Integration Record at Setup > Integration > Manage Integrations. If the field is populated and your server IP is not in it, add it or remove the restriction.
Cause 4: Script deployment not accepting external calls
Each RESTlet deployment has an "External Access" setting. If this is disabled, external calls fail at authentication rather than returning a useful script-level error.
Go to Customization > Scripting > Scripts, open the RESTlet, and check its deployments. The deployment status must be "Released" and the external access flag must be enabled.
Cause 5: Missing "Log in using Access Tokens" permission
The role assigned to the token must have the "Log in using Access Tokens" permission under Setup > Permissions > Setup. Without it, TBA authentication fails regardless of whether the token is valid.
Open the role at Setup > Users/Roles > Manage Roles, go to the Permissions tab, and check the Setup subtab.
invalid_grant (OAuth 2.0)
This error comes from NetSuite's OAuth 2.0 token endpoint, whether the token is for REST Web Services or a RESTlet. It means the token request, or the refresh, is no longer valid.
Authorization Code flow
The authorization code itself expires after a short window (typically 10 minutes). Once exchanged, the resulting access token expires after 60 minutes. The refresh token expires after 7 days.
If you see invalid_grant on a refresh request, the 7-day window has passed and the user needs to reauthorise. Design your integration to detect this and prompt reauthorisation rather than silently failing.
Client Credentials flow
Client Credentials tokens do not use refresh tokens. A new token is requested directly for each session using a signed JWT assertion. invalid_grant here usually means:
- The Integration Record was disabled or the client credentials grant was unchecked.
- The certificate uploaded to NetSuite has expired or been replaced, so the JWT assertion can no longer be verified.
- The private key used to sign the assertion does not match the public key on record.
Re-upload the certificate at Setup > Integration > OAuth 2.0 Client Credentials (Machine to Machine) Setup and generate a new token.
OAuth 2.0 calls to a RESTlet returning 401 or 403
When the token endpoint issues a token happily but the RESTlet call itself is rejected, check three things:
- The RESTlets scope. The Integration Record has separate scope checkboxes for REST Web Services and RESTlets. A token issued without the RESTlets scope authenticates fine at the token endpoint and then fails on the RESTlet call.
- The role permission. The role needs "Log in using OAuth 2.0 Access Tokens" under Setup > Permissions > Setup. This is a different permission from the TBA equivalent ("Log in using Access Tokens"); migrating an integration from TBA to OAuth 2.0 means adding it.
- Web Services Only roles. A role with the Web Services Only restriction cannot call RESTlets at all. It works for REST Web Services and fails for RESTlets, which is confusing when one integration uses both surfaces.
Missing role permissions
Even when authentication succeeds, the role assigned to the token may not have the permissions the script needs to run. The result is a 403, or a script-level error in the execution log.
Check the execution log at Customization > Scripting > Script Execution Logs. A permission error at the script level will appear here even if the call completes the authentication stage.
Common gaps:
- The role lacks read or write access to the record types the script accesses. Check Transactions, Lists, and Reports permission tabs in the role.
- The role cannot access the subsidiary or department involved in the transaction.
- The role does not have SuiteScript permission (Setup > Permissions > Setup > SuiteScript).
The minimum permissions for an integration role: Log in using Access Tokens, SuiteScript, and whichever record-level access the script needs. Do not use an admin role for integrations.
RESTlet scope issues
These aren't authentication errors, but they look like one from outside: the integration stops working, and neither side produces a clear error.
Wrong URL format
A RESTlet URL looks like:
https://[account-id].restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=[script-id]&deploy=[deploy-id]The script parameter takes the internal ID of the script record, not the script file name. The deploy parameter takes the deployment internal ID. Verify both in Customization > Scripting > Scripts.
Using the script file name or an incorrect deploy ID produces a 404 or a malformed response, which can look like an auth error to the calling system.
Deployment not released
A deployment in "Testing" status is only accessible to logged-in users. External calls require status "Released". Check the deployment record and change the status if needed.
GET versus POST mismatch
RESTlets define separate handler functions for GET, POST, PUT, and DELETE. Calling with the wrong HTTP method produces an error at the script level, not the auth level, but it is easy to misread in the calling system's logs. Confirm that your calling system's HTTP method matches the function the RESTlet exposes.
Tracing authentication failures in practice
Authentication failures produce no execution log entry, because the script never runs. The Login Audit Trail does record them: Setup > Users/Roles > User Management > View Login Audit Trail, where the Detail column names the reason for each failed attempt. Check it first when the execution log is empty.
On the calling side, log the full HTTP response: status code, headers, and body. A TBA auth failure returns a JSON body with "type": "error.type.invalidcredentials" or similar. This is more useful than the generic "connection refused" message some middleware platforms surface.
If the failure is intermittent, check whether it correlates with role changes, admin maintenance windows, or IP address changes on the calling server. Intermittent INVALID_LOGIN_ATTEMPT errors that clear on their own usually mean credentials were temporarily rotated and the integration eventually picked up the new values. The window when it fails can still cause data gaps, though.
The silent failure diagnosis post covers how to build alerting around authentication failures so they do not produce undetected data gaps.
Frequently asked questions
What causes INVALID_LOGIN_ATTEMPT when calling a NetSuite RESTlet?
The four most common causes: the token pair has been revoked (role change, admin rotation, or the integration record was edited); the consumer key or secret is wrong (copy-paste issue with the integration record credentials); the integration record has IP address restrictions that exclude your calling server; or the RESTlet script deployment has "External Access" disabled. Check the Access Tokens list at Setup > Users/Roles > Access Tokens to verify the token still exists and is still linked to the expected role.
Why does my RESTlet return a 401 after working fine for months?
The most common reason is token revocation. When a user's role is changed or removed, any access tokens tied to that user-role combination are automatically invalidated. If your integration authenticates as a named user rather than a dedicated integration user, a routine role change can silently break it. Move to a dedicated integration role with only the permissions the script needs.
Does NetSuite support OAuth 2.0 for calling RESTlets from external systems?
Yes. RESTlets accept both TBA (OAuth 1.0a signed requests) and OAuth 2.0 bearer tokens. For OAuth 2.0, the integration record must have the RESTlets scope enabled, the role needs the "Log in using OAuth 2.0 Access Tokens" permission, and each call carries an Authorization: Bearer header. One restriction: roles with the Web Services Only restriction do not work with RESTlets.
What does 'invalid_grant' mean in a NetSuite OAuth 2.0 context?
It means the token has expired or was revoked. For Authorization Code flows, the token expires after 7 days and the user needs to reauthorise. For Client Credentials flows it typically means the Integration Record was modified or disabled after the token was issued, or the key used to sign the JWT assertion no longer matches the public key uploaded to NetSuite. Re-upload the certificate and generate a new token.
If an integration is producing auth errors you cannot diagnose from this list, book a technical review. Bring the HTTP response body and the execution log if there is one. It also helps to know what changed around the time it started failing.
Have a specific problem in mind?
A 30-minute technical review call to understand what's in your codebase and whether this is the right fit.
Book a technical review