Overview
The native integration sent detection.opened events straight to a ServiceNow table. With Feeds, you choose which tickets are delivered, and a small script in ServiceNow turns each ticket into an Incident record.
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 tickets to an endpoint you create in ServiceNow. Closest to the native integration, but 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.
Prerequisites
A ServiceNow admin user (or web_service_admin for Push), a manager user on the Axur Platform to create the feed, and an Axur Platform API Key (for Pull).
Create the feed in the Axur Platform
Go to one.axur.com/api-integrations → Feeds section → Add feed → Skip templates.
Choose the Tickets type.
Choose the delivery mode: Pull or Push. For Push, enter the endpoint URL you create in step 6.
Under Event date, select First detection (
ticket.creation.date). Each ticket arrives only once, just like the native integration'sdetection.opened.Under Feed parameters, leave it blank to receive everything, or filter, e.g.
type=phishing,fake-social-media-profile.Set a title, e.g. “ServiceNow – Incidents”, and save. Write down the feed ID/URL.
Figure 2: Suggested feed configuration (illustration; the actual screen may look different).
Also want re-detections?
Use Any detection (open.date). The script below updates the existing incident by ticketKey (correlation_id), so no incident is ever duplicated.
Field mapping
Each item collectionData.tickets[] 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. |
|
| Same as the native integration. |
| Workspace - Type - | Same format as the native integration. Capped at 160 characters: only the URL is shortened (with |
| Native template (Figure 3b) | Link: |
Watch out for dotted keys
Fields like open.date and creation.date have a dot in their name. In JavaScript, use brackets: d['open.date'], not d.open.date.
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.
In ServiceNow, go to System Definition › Script Includes → New.
Name:
AxurFeedMapper(must be exactly this name).Paste the code below into the Script field and save.
Figure 4: Creating the Script Include (illustration).
TYPE_LABELS and WORKSPACES hold the official labels used by the Axur Platform, so the text matches the native integration. Types not in the list fall back to Axur Surface the raw slug, so nothing breaks.
var AxurFeedMapper = {
TABLE: 'incident',
TICKET_URL: 'https://one.axur.com/brand-protection/tickets/',
SHORT_DESCRIPTION_LIMIT: 160,
DEFAULT_WORKSPACE: 'Axur Surface',
WORKSPACES: {
'digital-fraud': 'Digital Fraud',
'online-piracy': 'Online Piracy',
'data-leakage': 'Data Leakage',
'deep-dark-web': 'Deep & Dark Web',
'executives-vips': 'Executives & VIPs'
},
TYPE_LABELS: {
'phishing': { workspace: 'digital-fraud', name: 'Phishing' },
'paid-search': { workspace: 'digital-fraud', name: 'Brand use in paid search' },
'malware': { workspace: 'digital-fraud', name: 'Malware' },
'fake-social-media-profile': { workspace: 'digital-fraud', name: 'Fake social media profile' },
'similar-domain-name': { workspace: 'digital-fraud', name: 'Similar domain name' },
'fake-mobile-app': { workspace: 'digital-fraud', name: 'Fake mobile app' },
'fraudulent-brand-use': { workspace: 'digital-fraud', name: 'Fraudulent brand use' },
'unauthorized-sale': { workspace: 'online-piracy', name: 'Counterfeit or irregular sale' },
'unauthorized-distribution': { workspace: 'online-piracy', name: 'Content piracy' },
'corporate-credential-leak': { workspace: 'data-leakage', name: 'Corporate credential leak' },
'code-secret-leak': { workspace: 'data-leakage', name: 'Code secret leak' },
'database-exposure': { workspace: 'data-leakage', name: 'Database exposure' },
'document-leak': { workspace: 'data-leakage', name: 'Document leak' },
'other-sensitive-data': { workspace: 'data-leakage', name: 'Other sensitive data' },
'infostealer-credential': { workspace: 'data-leakage', name: 'Infostealer credentials' },
'dw-activity': { workspace: 'deep-dark-web', name: 'Deep & Dark Web activity' },
'data-exposure-website': { workspace: 'deep-dark-web', name: 'Data exposure in website' },
'data-sale-website': { workspace: 'deep-dark-web', name: 'Data sale in website' },
'fraud-tool-scheme-website': { workspace: 'deep-dark-web', name: 'Fraud tool or scheme in website' },
'suspicious-activity-website': { workspace: 'deep-dark-web', name: 'Suspicious activity in website' },
'data-exposure-message': { workspace: 'deep-dark-web', name: 'Data exposure in messaging app' },
'data-sale-message': { workspace: 'deep-dark-web', name: 'Data sale in messaging app' },
'fraud-tool-scheme-message': { workspace: 'deep-dark-web', name: 'Fraud tool or scheme in messaging app' },
'suspicious-activity-message': { workspace: 'deep-dark-web', name: 'Suspicious activity in messaging app' },
'infrastructure-exposure': { workspace: 'deep-dark-web', name: 'Infrastructure exposure' },
'ransomware-attack': { workspace: 'deep-dark-web', name: 'Ransomware attack alert' },
'executive-fake-social-media-profile': { workspace: 'executives-vips', name: 'Fake social media profile' },
'executive-personalinfo-leak': { workspace: 'executives-vips', name: 'Personal information leak' },
'executive-credential-leak': { workspace: 'executives-vips', name: 'Credential leak' },
'executive-mobile-phone': { workspace: 'executives-vips', name: 'Phone number leak' },
'executive-card-leak': { workspace: 'executives-vips', name: 'Credit card leak' }
},
buildFields: function (item) {
var t = item.ticket || {};
var d = item.detection || {};
var ticketKey = t.ticketKey;
var type = d.type || '';
var label = this.TYPE_LABELS[type] || { workspace: '', name: type };
var workspace = this.WORKSPACES[label.workspace] || this.DEFAULT_WORKSPACE;
var threatUrl = t.reference || '';
var prefix = workspace + ' - ' + label.name + ' - ' + t.customerKey + ' - ';
var suffix = ' (' + ticketKey + ')';
var shortDescription = prefix + this.fitReference(threatUrl, prefix.length + suffix.length) + suffix;
var description = [
'Axur - one.axur.com',
'',
'New threat detected (' + this.TICKET_URL + ticketKey + ')',
'',
'Ticket Key: ' + ticketKey,
this.assetsLine(d.assets || []),
'Ticket Type: ' + label.name + ' (' + type + ')',
'Threat URL: ' + threatUrl,
'Detection Date: ' + this.formatDate(d['open.date'] || t['creation.date'])
].join('\n');
return { ticketKey: ticketKey, shortDescription: shortDescription, description: description };
},
fitReference: function (reference, usedLength) {
var available = this.SHORT_DESCRIPTION_LIMIT - usedLength;
if (reference.length <= available) return reference;
return reference.substring(0, Math.max(available - 3, 0)) + '...';
},
assetsLine: function (assets) {
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.ticketKey) return;
var gr = new GlideRecord(this.TABLE);
gr.addQuery('correlation_id', f.ticketKey);
gr.query();
var exists = gr.next();
if (!exists) {
gr.initialize();
gr.state = 1;
}
gr.correlation_id = f.ticketKey;
gr.correlation_display = f.ticketKey;
gr.short_description = f.shortDescription;
gr.description = f.description;
if (exists) { gr.update(); } else { gr.insert(); }
},
processPayload: function (payload) {
var tickets = (payload && payload.collectionData && payload.collectionData.tickets) || [];
for (var i = 0; i < tickets.length; i++) {
this.upsert(tickets[i]);
}
return tickets.length;
}
};Different table?
If records should not go to incident, change the TABLE value. The table needs the correlation_id, short_description and description fields.
Option A: Pull with a Scheduled Job
Pick this option if your instance cannot receive external calls.
Store the URL and API Key
Go to
sys_properties.list(type it in the navigation filter) → New.Create
axur.feed.urlwith the feed URL, e.g. https://api.axur.com/gateway/1.0/api/integration-feed/feeds/feed/<ID.>Create
axur.feed.api_keywith the Axur Platform API Key and restrict Read roles toadmin.
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
Go to
System Definition › Scheduled Jobs→ New → Automatically run a script of your choosing.Name:
Axur Feed → Incident. Run: Periodically. Repeat interval:00:05:00(5 min).Paste the script below and save.
Figure 5: Scheduled Job running every 5 minutes (illustration).
var FEED_URL = gs.getProperty('axur.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 feed: HTTP ' + response.getStatusCode() + ' - ' + response.getBody());
break;
}
var processed = AxurFeedMapper.processPayload(JSON.parse(response.getBody()));
if (processed === 0) break;
}
Each call to the feed moves the pointer forward and returns the next page. The loop runs until a page comes back empty, capped at 20 pages per run so the job never hangs. See the API documentation for the authentication header format.
Option B: Push with a Scripted REST API
Pick this option to receive tickets in near real-time. Your instance must accept inbound calls from the Axur Platform.
Go to System Web Services › Scripted Web Services › Scripted REST APIs → New.
Name:
Axur Feed, API ID:axur_feed. Save.On the Resources tab → New: Name
tickets, HTTP methodPOST, Relative path/tickets.Paste the script below into the resource and save.
Copy the Resource path (e.g.
https://<instance>.service-now.com/api/<scope>/axur_feed/tickets) and enter it as the Push feed endpoint in the Axur Platform.
Figure 6: POST resource that receives Webhook 2.0 (illustration).
(function process(request, response) {
var processed = AxurFeedMapper.processPayload(request.body.data);
response.setStatus(200);
response.setBody({ processed: processed });
})(request, response);Endpoint authentication
By default, the resource requires ServiceNow authentication (Requires authentication). Set it up according to the authentication options available in the Axur Platform Push feed (e.g. a dedicated integration user that can only write to the Incident table). Do not disable authentication.
Test and validate
Test without moving the pointer: for Pull, append
?dry-run=truetoaxur.feed.url. You get the same tickets every time and nothing is lost.Run the job manually: open the Scheduled Job → Execute Now.
Check the incidents under Incident › All, filtering by a non-empty Correlation ID.
Run it again. Thanks to the upsert, the number of incidents should not grow.
Check for errors under System Logs › Errors (messages starting with
Axur feed:).All good? Remove
?dry-run=trueso the pointer starts moving forward.
Tip
Validate on a sub-production or development instance before enabling it in production.
Limitations and checklist
Native integration | With Feeds |
| ✅ Covered (event date First detection or Any detection) |
| ✅ Same format, built by the script |
New detections only | ✅ And more: filters by type, incident, takedown, any update… |
Go-live checklist
Feed created in the Axur Platform with the right event date and filters
AxurFeedMapperScript Include createdTYPE_LABELStable updated with the types you usePull:
axur.feed.urlandaxur.feed.api_keyproperties created (read-restricted)Pull: Scheduled Job active · Push: endpoint registered on the feed
Tested with
?dry-run=true, no duplicates?dry-run=trueremoved






