How to Add a Credential Test to an n8n Community Node
A credential test gives n8n a small request it can run when a user saves a credential. For a community node, define the credential class, map the saved fields through authenticate, and add a test.request that reaches a safe provider endpoint. The node can then catch a bad key or wrong host before a workflow operation fails.
The test request should prove authentication with the least data and side effects possible. A read-only account or profile endpoint is usually easier to reason about than a list endpoint that returns thousands of records or an endpoint that changes state.
The NativeShip n8n credentials guide covers the wider API key, OAuth2, and credential setup decisions. This post stays on the implementation boundary: the request n8n uses to test the credential and the checks around it.
When a user clicks Save on a credential, n8n runs this sequence:
- Reads the saved field values from the Credentials modal.
- Applies your
authenticatemapping to build an authenticated request. - Sends
test.requestto the provider. - Shows "Connection tested successfully" on a 2xx response (and any
rulesyou add pass), or the provider's error otherwise.
Know what the credential test owns
A credential file has separate jobs:
| Part | Job | What to verify |
|---|---|---|
properties |
Shows fields in the Credentials modal and stores their values | Names, types, defaults, required values, and any base URL or tenant field |
authenticate |
Places credential values in the request | Header, query string, body, or Basic Auth mapping |
test.request |
Gives n8n a request for checking the saved credential | A safe URL, the right base URL, and an endpoint that needs the same authentication |
Node credentials array |
Connects the node to the credential class | The exact internal credential name |
The test does not prove that every operation works. It checks one authenticated request. Keep operation-specific parameters, response mapping, pagination, and business permissions in their own tests.
Start with the provider's authentication contract
Before writing TypeScript, write down the provider's actual authentication contract. Check its API documentation for the header or query key, the base URL, the required version prefix, and a low-risk endpoint that accepts the credential.
| Provider contract | authenticate mapping |
Test concern |
|---|---|---|
| API key in a header | header |
Use the exact header spelling and prefix |
| Bearer token | header.Authorization |
Confirm whether the value includes Bearer |
| API key in the query string | qs |
Avoid putting secrets in URLs unless the provider requires it |
| Basic Auth | auth with username and password |
Use an endpoint that supports Basic Auth |
| Token in the request body | body |
Confirm that the test endpoint accepts the same method and body |
The current n8n credentials-file reference documents properties, authenticate, and test. It shows generic authentication mappings for headers, query strings, bodies, and Basic Auth, then places the test URL in test.request.
Do not copy an authentication field from a different service. A credential can look correct in the editor and still fail because the provider expects a different header name, token prefix, host, or API version.
Add the smallest useful credential class
The following is a pattern to adapt to the target API's real authentication contract. The NativeShip custom-node guide covers the surrounding package structure.
import {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ExampleApi implements ICredentialType {
name = 'exampleApi';
displayName = 'Example API';
documentationUrl = 'https://example.com/docs/auth';
properties: INodeProperties[] = [
{
displayName: 'API key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: 'https://api.example.com',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
header: {
'X-API-Key': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials?.baseUrl}}',
url: '/v1/account',
},
};
}
Mark every secret field with typeOptions: { password: true } so n8n masks it in the editor; the n8n linter flags secret fields without it, and the verified node checklist treats lint as a submission gate. The field names in properties become the names used in the expressions. In this example, authenticate reads apiKey, and test.request reads baseUrl. If the provider has one fixed host, remove the editable base URL and set the documented host in the request or node configuration instead.
Use the actual provider endpoint in place of /v1/account. The endpoint needs to be safe to call repeatedly and should require the same credential that normal node operations use.
Check the response body, not only the status
Some APIs return 200 OK with an error object when a key is wrong. Add rules to the test so n8n inspects the body:
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials?.baseUrl}}',
url: '/v1/account',
},
rules: [
{
type: 'responseSuccessBody',
properties: {
key: 'error.code',
value: 'invalid_api_key',
message: 'The API key is not valid. Create a new key in your account settings.',
},
},
],
};
If the value at key matches value, n8n marks the test as failed and shows your message. Write that message for the person in the Credentials modal: say what is wrong and where to fix it.
OAuth2 credentials
For OAuth2, extend n8n's built-in type with extends = ['oAuth2Api'] and set the authorization URL, token URL, and scopes as hidden properties. Connecting the account already runs the token exchange, so an extra test.request is optional. Add one only if a token can be issued while API access is still missing, for example on a plan without API access. The credentials guide compares API key and OAuth2 setups.
Make the test use the same authentication as the node
The test request is only useful when its authentication mapping matches the requests made by the node. If normal operations send X-API-Key, the test should reach an endpoint that accepts that header. If the provider uses a bearer token, include the correct prefix in authenticate and let the test request use that mapping.
Avoid adding a second copy of the secret inside test.request. Keep credential values in authenticate so the same mapping applies to the test and the node's API requests.
Check these details together:
- Does the field name in the expression match the
propertiesname? - Does the auth location match the provider documentation?
- Does the test endpoint use the same host and API version as the operation?
- Does the test require a tenant, workspace, or region value that the credential class does not collect?
- Does the provider reject a token that can call normal endpoints but cannot call the test endpoint?
A credential test can fail because the endpoint needs a permission the normal operation does not need. Choose a test endpoint whose access requirement matches the minimum permission the node promises to support.
Keep the credential name aligned with the node
The credential class's internal name must match the name in the node's credentials array:
credentials: [
{
name: 'exampleApi',
required: true,
},
],
The n8n credential troubleshooting guide calls out this exact mismatch. If the class uses exampleApi but the node references exampleAPI, n8n cannot resolve the credential type.
Check the same name in every place that registers the credential:
- The credential class's
name. - The node description's
credentials[].name. - The package metadata that registers the node and credential files.
- Any
testedByor custom test method name, when the implementation uses one.
A name mismatch is a registration problem. Changing the endpoint will not fix it.
Choose a test endpoint that can explain failure
An endpoint that returns a small account or identity response is useful because it can distinguish authentication from an operation's input requirements. Record the provider's expected response and status behavior before you write error copy.
| Result while testing | What to inspect next |
|---|---|
| Success | Confirm the returned response does not contain secret values and that the saved credential can run a real read operation |
| 401 or similar authentication failure | Check the token value, prefix, header name, and whether the key is expired or revoked |
| 403 or similar permission failure | Check scopes, workspace access, and whether the test endpoint needs more permission than the node's first operation |
| 404 | Check base URL, version prefix, path, region, and whether the endpoint exists for the account |
| Timeout or network error | Check the host, DNS, TLS, proxy, and self-hosted network path |
| A valid response with unexpected shape | Confirm that the endpoint is the provider's credential-check route, not a normal business response |
Treat those rows as debugging directions, not universal n8n status rules. The provider's API contract and the supported n8n version decide how a particular error appears in the editor.
Keep base URLs and paths deliberate
A base URL field helps when the provider has regional or tenant-specific hosts. It also creates another input that the user can mistype. Choose one of these patterns and document it:
- Fixed host: keep the provider host in the node or credential definition and ask only for the secret.
- User-supplied host: collect a complete host, validate the value, and use it consistently in
test.requestand operation requests. - Host plus version prefix: decide whether the base URL owns
/v1or each operation owns it. Do not add the prefix in both places.
Test a base URL with and without a trailing slash if the supported n8n request path can receive both forms. Also test a regional host and a wrong host so the editor error points to the real configuration problem.
When the service uses a tenant ID or workspace ID, include that field in the credential only if every request, including the test, needs it. A test that ignores a required tenant field can pass while the first operation fails.
Know when a request object is not enough
The test.request form is a good fit when one authenticated HTTP request can verify the credential. It becomes a poor fit when the provider requires a signed request assembled in code, a token exchange with several calls, a local certificate, or a test decision based on a custom response.
For those cases, add a custom test method in the node and point the credential entry at it with testedBy:
credentials: [{ name: 'exampleApi', required: true, testedBy: 'exampleApiTest' }],
methods = {
credentialTest: {
async exampleApiTest(
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> {
try {
await signAndCallIdentityEndpoint(this, credential.data);
return { status: 'OK', message: 'Connection successful' };
} catch (error) {
return { status: 'Error', message: (error as Error).message };
}
},
},
};
Keep that method small, return a clear success or failure result, and do not broaden it into an operation runner. If you plan to submit for verification, the method must not read environment variables or the file system, and it cannot pull in a runtime dependency for signing. The n8n-node tool documentation explains the current project scaffolding and local development commands; use the same supported version when you test a custom credential path.
Do not switch to a custom test method to hide a wrong endpoint or mismatched credential name. Fix the request and registration contract first.
Test the credential before testing operations
Run the credential test in a local n8n instance with a matrix that separates configuration errors from API behavior:
| Test | Evidence to record |
|---|---|
| Valid credential | Test response, n8n version, package version, and endpoint |
| Invalid or revoked secret | Error status and safe editor message |
| Missing required field | Credential form validation and test result |
| Wrong base URL or version | Resolved request and provider response |
| Insufficient permission | Scope or workspace setting and returned status |
| Regional or tenant host | Host value, request path, and result |
| Credential name mismatch | Registration error before changing the endpoint |
| First real read operation | Operation request, response shape, and whether the same credential works |
| Secret in an error or log | Confirm it is redacted before sharing evidence |
Start the instance with npm run dev. From n8n 3.0, n8n-node dev runs n8n in a container, so install Docker or Podman, or run n8n-node dev --external-n8n against your own instance started with N8N_DEV_RELOAD=true. Use the NativeShip custom-node debugging guide to inspect the resolved request and registration path when a local test fails. Keep API keys, bearer tokens, cookies, and private response data out of screenshots and logs.
Once it passes, explain the credential setup in your README (where to find the key, which plan or scope it needs); the README checklist shows what to include. A passing credential test is one gate in node development. It gives the editor a reliable check for the saved authentication details, while each operation still needs its own request, permission, response, and error test.
Frequently asked questions
It gives n8n a request to run when the user saves the credential, to check that the saved values can authenticate with the provider. The request goes in test.request, and optional rules let n8n flag a 200 response whose body still reports an error.
authenticate tells n8n where to place credential values on a request. test.request tells n8n which URL to call for the credential check.
No. It checks one authenticated request. Each operation still needs tests for its parameters, permissions, response shape, pagination, and provider errors.
Use a small, read-only endpoint that requires the same authentication and minimum permission as the node. Account or identity endpoints are common choices when the provider documents them.
The credential class name and the node's credentials array name do not match, or the package did not register the credential file. Check the internal name in every registration point.
Use one when a single request object cannot represent the provider's signing, token exchange, certificate, or multi-request flow. Add a credentialTest method to the node, reference it with testedBy in the credentials array, and return { status: 'OK' } or { status: 'Error', message }.
Run the test in a controlled local instance, redact keys and tokens from screenshots and logs, and record the endpoint, version, status, and safe error message instead of the credential value.