Overview
A Credentials Feed delivers the exposed credentials (leaked logins of employees or customers) that the Axur Platform detects. A small script in ServiceNow turns each credential into an Incident record, so your team can reset the password and track the response.
Figure 1: The two delivery modes. Both use the same JSON and the same mapping script.
Push (Webhook 2.0): near real-time
The Axur Platform sends credentials to an endpoint you create in ServiceNow. Your instance must accept inbound calls.
Pull (polling): easier to approve
ServiceNow queries the feed every X minutes. Traffic is outbound only, and latency equals the schedule interval.
Already using the Tickets Feed guide?
This guide follows the same structure, with its own script names (AxurCredentialFeedMapper, axur.credentials_feed.url). Both integrations can run side by side in the same instance and can share the same API Key property.
Prerequisites
A ServiceNow instance and an admin user (or
web_service_adminfor Push). To test first, use a free Personal Developer Instance (PDI) from developer.servicenow.com. Its URL looks likehttps://dev123456.service-now.com.A manager user on the Axur Platform to create the feed.
An Axur Platform API Key (Pull only). See step "Get the API Key (Pull only)".
Classic UI vs. Next Experience
ServiceNow menus look different in the classic interface (UI16) and in Next Experience (the All menu). Some shortcuts, such as typing sys_properties.list in the filter, do not work in every version. Every step below also gives a direct URL that works in both interfaces. Replace <instance> with your instance name, e.g. dev123456.
Direct URLs used in this guide
What | Direct URL |
New system property |
|
System properties list |
|
New Script Include |
|
New Scheduled Script Execution |
|
New Scripted REST API |
|
Incidents created by the feed |
|
System logs |
|
Get the feed and API Key from the Axur Platform
Do this step first. Pull needs both the feed URL and the API Key before you create anything in ServiceNow.
Create the feed
Go to one.axur.com/api-integrations → Feeds section → Add feed. You can pick the Employee credentials or User credentials template, or click Skip templates.
Choose the Credentials type.
Choose the delivery mode: Pull or Push. For Push, enter the endpoint URL you create in step 6.
Event date is pre-filled with the creation date (
created). Each credential arrives once, when the Axur Platform first detects it.Under Feed parameters, choose which credentials to receive. Combine filters with
&:Recommended: also add the
fieldsparameter shown below. The feed then returns only what the script uses, and passwords never leave the Axur Platform:fields=id,created,detectionDate,customer,assets,status,credential.types,user,user.type,access.url,source.name
Example combined with a filter:
credential.types=employee&fields=id,created,…Set a title, e.g. “ServiceNow – Exposed credentials”, and save.
Copy the feed URL shown for the feed in the list. It looks like
https://api.axur.com/gateway/1.0/api/integration-feed/feeds/feed/<ID>.
Figure 2: Suggested feed configuration (illustration; the actual screen may look different).
Get the API Key (Pull only)
On the Axur Platform, open My Preferences → API KEY tab.
Create a new key and copy it right away. Store it in a password manager until you create the ServiceNow property in "Store the URL and API Key".
The key belongs to the user who created it
The key only sees what that user can see on the Axur Platform. If the user is deactivated, blocked, or removed, the key is revoked and the job starts failing with 401 Unauthorized. Create the key with a user who has access to the credentials and will not be deactivated, such as a dedicated integration user. Never share the key or write it in scripts.
Field mapping
Each item in collectionData.detections[] becomes a record in the Incident table. The colors show where each piece of information comes from:
Mapping table
ServiceNow field | Source in the JSON | Notes |
| fixed | Set on creation only. Updates do not reopen the incident. |
|
| Used to avoid duplicates. |
|
|
|
| Data Leakage - Type - | Capped at 160 characters: only the user/URL part is shortened (with |
| Template in Figure 3b | Link: |
Credential type labels
| Label in the Incident |
| Employee credential exposed |
| User credential exposed |
both | Employee and User credential exposed |
The password is never written to ServiceNow
The script does not copy the password into the Incident, even if the feed sends it. Incidents are visible to many people and kept for a long time, so storing exposed passwords there would create a new leak. Your team can see the full detection on the Axur Platform through the link in the description.
Watch out for dotted keys
Fields like access.url and credential.types have a dot in their name. In JavaScript, use brackets: d['access.url'], not d.access.url.
Script Include: the mapper
This script builds the fields and creates or updates the incident. You create it once, and both Push and Pull use it.
Open
https://<instance>.service-now.com/sys_script_include.do?sys_id=-1. From the menu, the path is System Definition › Script Includes → New.Name:
AxurCredentialFeedMapper(must be exactly this name).Paste the code below into the Script field and save.
var AxurCredentialFeedMapper = {
TABLE: 'incident',
CREDENTIALS_URL: 'https://one.axur.com/data-leakage/credentials',
SHORT_DESCRIPTION_LIMIT: 160,
WORKSPACE: 'Data Leakage',
TYPE_LABELS: {
'employee': 'Employee',
'user': 'User'
},
buildFields: function (item) {
var id = item.id;
var customer = item.customer || '';
var user = item.user || '';
var accessUrl = item['access.url'] || '';
var typeLabel = this.typeLabel(item['credential.types'] || []);
var prefix = this.WORKSPACE + ' - ' + typeLabel + ' credential exposed - ' + customer + ' - ';
var target = accessUrl ? user + ' @ ' + accessUrl : user;
var shortDescription = prefix + this.fit(target, prefix.length);
var link = this.CREDENTIALS_URL + '?customerId=' + encodeURIComponent(customer)
+ '&text=' + encodeURIComponent(id);
var description = [
'Axur - one.axur.com',
'',
'New credential exposure detected (' + link + ')',
'',
'Detection ID: ' + id,
this.assetsLine(item.assets || []),
'Credential Type: ' + typeLabel,
'User: ' + user + (item['user.type'] ? ' (' + item['user.type'] + ')' : ''),
'Access URL: ' + accessUrl,
'Source: ' + (item['source.name'] || ''),
'Status: ' + (item.status || ''),
'Detection Date: ' + this.formatDate(item.detectionDate || item.created)
].join('\n');
return { id: id, shortDescription: shortDescription, description: description };
},
typeLabel: function (types) {
var labels = [];
for (var i = 0; i < types.length; i++) {
labels.push(this.TYPE_LABELS[types[i]] || types[i]);
}
return labels.length ? labels.join(' and ') : 'Unknown';
},
fit: function (text, usedLength) {
var available = this.SHORT_DESCRIPTION_LIMIT - usedLength;
if (text.length <= available) return text;
return text.substring(0, Math.max(available - 3, 0)) + '...';
},
assetsLine: function (assets) {
if (assets.length === 0) return 'Asset: -';
return assets.length === 1 ? 'Asset: ' + assets[0] : 'Assets: (' + assets.join(', ') + ')';
},
formatDate: function (iso) {
if (!iso) return '';
return iso.substring(8, 10) + '/' + iso.substring(5, 7) + '/' + iso.substring(0, 4);
},
upsert: function (item) {
var f = this.buildFields(item);
if (!f.id) return;
var gr = new GlideRecord(this.TABLE);
gr.addQuery('correlation_id', f.id);
gr.query();
var exists = gr.next();
if (!exists) {
gr.initialize();
gr.state = 1;
}
gr.correlation_id = f.id;
gr.correlation_display = f.id;
gr.short_description = f.shortDescription;
gr.description = f.description;
if (exists) { gr.update(); } else { gr.insert(); }
},
processPayload: function (payload) {
var detections = (payload && payload.collectionData && payload.collectionData.detections) || [];
for (var i = 0; i < detections.length; i++) {
this.upsert(detections[i]);
}
return detections.length;
}
};Option A: Pull with a Scheduled Job
Pick this option if your instance cannot receive external calls.
Store the URL and API Key
Open the new property form directly:
https://<instance>.service-now.com/sys_properties.do?sys_id=-1. You can also opensys_properties_list.doand click New.Create the feed URL property:
If
axur.feed.api_keydoes not exist yet (it is shared with the Tickets Feed integration), open the form again and create it:
Security
Never write the API Key directly in the script. If your security team prefers, use Connection & Credential Aliases instead of the system property.
Create the schedule
Open
https://<instance>.service-now.com/sysauto_script.do?sys_id=-1. From the menu, the path is System Definition › Scheduled Jobs → New → Automatically run a script of your choosing.Name:
Axur Credentials Feed → Incident. Run: Periodically. Repeat interval:00:05:00(5 min).Paste the script below and save.
Figure 4: Scheduled Job running every 5 minutes (illustration).
var FEED_URL = gs.getProperty('axur.credentials_feed.url');
var API_KEY = gs.getProperty('axur.feed.api_key');
var MAX_PAGES = 20;
for (var page = 0; page < MAX_PAGES; page++) {
var request = new sn_ws.RESTMessageV2();
request.setEndpoint(FEED_URL);
request.setHttpMethod('GET');
request.setRequestHeader('Authorization', 'Bearer ' + API_KEY);
request.setRequestHeader('Accept', 'application/json');
var response = request.execute();
if (response.getStatusCode() != 200) {
gs.error('Axur credentials feed: HTTP ' + response.getStatusCode() + ' - ' + response.getBody());
break;
}
var processed = AxurCredentialFeedMapper.processPayload(JSON.parse(response.getBody()));
if (processed === 0 || FEED_URL.indexOf('dry-run=true') > -1) break;
}
Each call to the feed moves the pointer forward and returns the next batch. The loop runs until a batch comes back empty, capped at 20 calls per run so the job never hangs. In dry-run mode the pointer does not move, so the job makes a single call per run.
Option B: Push with a Scripted REST API
Pick this option to receive credentials in near real-time. Your instance must accept inbound calls from the Axur Platform.
Open
https://<instance>.service-now.com/sys_ws_definition.do?sys_id=-1. From the menu, the path is System Web Services › Scripted Web Services › Scripted REST APIs → New.Name:
Axur Credentials Feed, API ID:axur_credentials_feed. Save.On the Resources tab → New: Name
credentials, HTTP methodPOST, Relative path/credentials.Paste the script below into the resource and save.
Copy the Resource path (e.g.
https://<instance>.service-now.com/api/<scope>/axur_credentials_feed/credentials) and enter it as the Push feed endpoint in the Axur Platform.
(function process(request, response) {
var processed = AxurCredentialFeedMapper.processPayload(request.body.data);
response.setStatus(200);
response.setBody({ processed: processed });
})(request, response);
Endpoint authentication
The resource requires ServiceNow authentication by default (Requires authentication). Do not disable it. Instead, let the Axur Platform authenticate on every call:
In ServiceNow, create a dedicated integration user (e.g.
axur.integration) with only the roles needed to create and update incidents.When you configure the Push feed on the Axur Platform, add a custom header with that user's credentials:
Authorization: Basic <base64 of user:password>. The Axur Platform sends your custom headers on every request.
Optional: verify the signature
Every Push request also carries an X-Push-Signature header: an HMAC-SHA256 of the raw request body, hex-encoded, computed with the secret key of the Push feed. Validate it in the resource if your security team requires proof that the call came from the Axur Platform.
Connection test
When you save or test the Push feed, the Axur Platform sends an empty body ({}). The script answers 200 with processed: 0 and creates nothing.
Test and validate
Turn on dry-run temporarily: open the
axur.credentials_feed.urlproperty (sys_properties_list.do, search by name) and add?dry-run=trueto the end of its Value, e.g.https://api.axur.com/gateway/1.0/api/integration-feed/feeds/feed/<ID>?dry-run=true. Save. The feed now returns the same credentials on every call and its pointer does not move, so nothing is lost while you test.Run the job manually: open the Scheduled Job → Execute Now.
Check the incidents at
incident_list.do?sysparm_query=correlation_idISNOTEMPTY. Confirm that no password appears in the Incident.Run it again. Thanks to the upsert, the number of incidents should not grow.
Check for errors at
syslog_list.door under System Logs › Errors (messages starting withAxur credentials feed:).All good? Edit
axur.credentials_feed.urlagain and remove?dry-run=truefrom its value, so the pointer starts moving forward.
Tip
Validate on a sub-production or development instance before enabling it in production. A feed without filters can bring many credentials on the first run; start with a filter (e.g. credential.types=employee) if you expect a large volume.
Security notes and checklist
Topic | Recommendation |
Passwords | Never stored in ServiceNow. Use the |
Who sees the incidents | Exposed credentials are personal data. Assign the incidents to a restricted group (e.g. with an Assignment group rule) instead of the general queue. |
API Key | Stored as |
Go-live checklist
Credentials feed created in the Axur Platform with the right filters and the
fieldsparameterAxurCredentialFeedMapperScript Include createdPull: properties
axur.credentials_feed.urlandaxur.feed.api_keycreatedPull: Scheduled Job active · Push: endpoint registered on the feed
Tested with
?dry-run=trueinaxur.credentials_feed.url, no duplicates, no passwords in the Incident?dry-run=trueremoved fromaxur.credentials_feed.url







