Appearance
Modules and Components
Authentication
Trieste uses JWT for authentication. When authenticated, a token and refresh token is provided. The refresh token is long lived while the token is set to 24 hours. Once the token expires, the refresh token can be used to get a new token.
Getting tokens
POST /v1/auth/login
json
Request
{
"email": "user@example.com",
"password": "password"
}
Response
{
"token": "xxx",
"refresh_token": "xxx",
"user": {}
}Getting refresh tokens
POST /v1/auth/refresh-tokens
json
Request
{
"access_token": "xxx",
"refresh_token": "xxx"
}
Response
{
"token": "xxx",
"refresh_token": "xxx",
"user": {}
}Database configuration
Databases are registered in the app/Config/database.js file.
A 'default' config is expected as this is the main database used for the application, however additional databases can be registered as so.
typescript
export default {
default: {
username: getEnv('LEADS_DB_USERNAME'),
password: getEnv('LEADS_DB_PASSWORD'),
host: getEnv('LEADS_DB_HOST'),
port: getEnv('LEADS_DB_PORT'),
db: getEnv('LEADS_DB_TABLE'),
dialect: getEnv('LEADS_DB_DIALECT', 'mysql'),
schema_path: appPath('Database/Schema'),
model_path: appPath('Database/Models'),
default_model: appPath('Database/Models/Base.js'),
type: 'sequelize',
underscored: true,
created_at: 'created_at',
deleted_at: 'deleted_at',
updated_at: 'updated_at',
pool: {
min: 1,
max: 100
}
},
legacy: {
db: getEnv('V1_DB_TABLE'),
username: getEnv('V1_DB_USERNAME'),
password: getEnv('V1_DB_PASSWORD'),
host: getEnv('V1_DB_HOST'),
port: getEnv('V1_DB_PORT'),
dialect: getEnv('DB_DIALECT', 'mysql'),
model_path: appPath('Database/Legacy/Models'),
schema_path: appPath('Database/Legacy/Schema'),
type: 'sequelize'
}
}Schema directory
For each database registered, a schema directory is expected to be defined. This is where the sequelize database schema is defined.
For example, for the default database to register the campaigns table, the schema file can be found in app/Database/Schema/campaigns.js
typescript
export class campaigns extends Model<campaignsAttributes, campaignsCreationAttributes> implements campaignsAttributes {
id!: number;
uuid?: string;
name?: string;
description?: string;
status?: string;
created_by_id?: number;
updated_by_id?: number;
owner_id?: number;
site_id?: number;
company_id?: number;
default_project_id?: number;
created_at?: Date;
updated_at?: Date;
deleted_at?: Date;
legacy_id?: number;
v1_sync_status?: number;
static initModel(sequelize: Sequelize.Sequelize): typeof campaigns {
return sequelize.define('campaigns', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
uuid: {
type: DataTypes.STRING(255),
allowNull: true
},
name: {
type: DataTypes.STRING(255),
allowNull: true
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
status: {
type: DataTypes.STRING(255),
allowNull: true
},
created_by_id: {
type: DataTypes.INTEGER,
allowNull: true
},
updated_by_id: {
type: DataTypes.INTEGER,
allowNull: true
},
owner_id: {
type: DataTypes.INTEGER,
allowNull: true
},
site_id: {
type: DataTypes.INTEGER,
allowNull: true
},
company_id: {
type: DataTypes.INTEGER,
allowNull: true
},
default_project_id: {
type: DataTypes.INTEGER,
allowNull: true
},
legacy_id: {
type: DataTypes.INTEGER,
allowNull: true
},
v1_sync_status: {
type: DataTypes.INTEGER,
allowNull: true
}
}, {
tableName: 'campaigns',
timestamps: true,
paranoid: true,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
{
name: "campaigns_legacy_id_v1_sync_status",
using: "BTREE",
fields: [
{ name: "legacy_id" },
{ name: "v1_sync_status" },
]
},
]
}) as typeof campaigns;
}
}Database models
To further extend each database model, a model file with the same name can be defined in the models directory that will automatically be binded to a schema file with the same name.
This can be used to define the relationships between models, hooks between database actions and any custom functions desired.
In the same case with the campaigns table, the model file can be found in app/Database/Models/campaigns.js
typescript
export default class Campaigns extends base {
setup () {
vm = this
// defines a function available to the model itself. e.g db.models.campaigns.getOneWithMostAvailability()
this.model.getOneWithMostAvailability = getOneWithMostAvailability
/* defines a function available to a models instance. e.g.
const item = await db.models.campaigns.qb().one();
item.decrease();
*/
this.model.prototype.decrease = decrease
this.model.hasOne(this.models.linkLeadsBatches, {
as: 'link_lead',
foreignKey: 'campaign_id',
sourceKey: 'id',
onDelete: 'NO ACTION',
onUpdate: 'NO ACTION'
})
this.model.hasOne(this.models.campaignBatchJobs, {
as: 'campaign_batch_job',
foreignKey: 'campaign_id',
sourceKey: 'id',
onDelete: 'NO ACTION',
onUpdate: 'NO ACTION'
})
this.model.beforeDestroy(this.onDelete)
this.model.beforeBulkDestroy(async (opts:any) => {
for (const campaign of await this.model.findAll(opts)) {
await this.onDelete(campaign, opts)
}
})
UuidOnCreate(this.model)
FindByUuidOrId(this.model)
}
async onDelete (campaign:DBDefault['models']['campaigns']['$PROTO'], opts:any = {}) {
// Delete all batch items
try {
await vm.models.linkLeadsBatches.destroy({
where: {
campaign_id: campaign.id
}
})
} catch (e) {
console.log('ERROR', e)
}
}
}Helpers
The app has a variety of helpers thanks to the core module. These are used to fetch DB's, configs and http (axios) clients
typescript
import '#app/Core/bootstrap.js';
/** Gets the database connection */
const db = await getDb(); // { sequelize: Sequelize connection, modes: Sequelize models }
/* The Db helper binds a qb() function to the models to simplify querying */
const leads = await db.models.leads.
qb(). // initiate query helper
eq('company_id', companyId). // where company_id = companyId
like('from_url', 'ayima.com'). // where from_url like '%ayima.com%'
lt('created_at', new Date()). // where created_at < new Date()
lte('created_at', new Date()). // where created_at <= new Date()
gt('created_at', new Date()). // where created_at > new Date()
gte('created_at', new Date()). // where created_at >= new Date()
between('created_at', new Date(), new Date()). // where created_at between new Date() and new Date()
notEq('company_id', 0). // where company_id != 0
with('emails'). // Includes email relationship
attributes('id', 'from_url'). // select id, from_url
all() // returns all results in an array. Alternatively .one() returns a single value.
const config = await getConfig('database.default'); // Gets config values from app/Config/database.js key: default
const ahrefs = await getConfig('clients.ahrefsV3'); // Gets the ahrefs axios client which is reusable without needing to set API keys.Workers
Trieste has 3 types of workers: Cron, Queue and isolated worker instances.
Isolated worker instance
The isolated worker is a separate instance of the app that runs indefinitely. This is used for tasks that need to run in the background and not be affected by the main app. This is useful for tasks that need to run indefinitely and not be affected by the main app.
Due to the nature of these, they do not have to specified in a specific directory. But for the sake of cleanliness and consistency, it is based this remain in the App/Jobs folder.
As these are isolated, each script needs to be called independently. This can be done in two ways:
PM2
For best practises and local development, each isolated worker is defined in the pm2 config found in app/Config/pm2.js
This allows for a pm2 environment to be easily executed running and maintaining each of the workers.
To run PM2, simply run the following command from the root directory: node pm2.config.js
Kubernetes
For production, each isolated workers is registered as an independent kubernetes service in the deployment.yaml file. This allows for each worker to be run independently and maintained by the kubernetes cluster.
E.g. for the worker found in app/Jobs/Pm2/FetchEmails.js, the following service would be defined in the deployment.yaml file:
yaml
# kubernetes/production/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: prod-trieste-v2-api-email-checker
spec:
selector:
matchLabels:
app: prod-trieste-v2-api-email-checker
replicas: 1
template:
metadata:
annotations:
buddyDeployGitRevision: "$BUDDY_EXECUTION_REVISION_SHORT"
buddyDeployUser: "$BUDDY_INVOKER_NAME"
buddyDeployTime: "$BUDDY_EXECUTION_START_DATE"
labels:
app: prod-trieste-v2-api-email-checker
spec:
containers:
- name: prod-trieste-v2-api-email-checker
imagePullPolicy: Always
image: 302327764052.dkr.ecr.eu-west-2.amazonaws.com/trieste-v2-api-service:$BUDDY_EXECUTION_REVISION_SHORT
command: ["node", "app/Jobs/Pm2/FetchEmails.js"]
envFrom:
- configMapRef:
name: prod-trieste-v2-api-config
- secretRef:
name: prod-trieste-v2-api-secrets
imagePullSecrets:
- name: regcredQueue
Trieste allows for the ability to have a queued based worker system using BullMQ and redis. This allows for workers that do not need to be run indefinitely or routinely to be run in a queue system. The benefit of using this system is that jobs/tasks are watched, so failure can be easily detected and fixed/restarted. This also enables the application to run multiple instances of the job in parallel.
The main (and only) example of this is the batch process.
How this works is the BullMQ worker is defined in a functions file:
typescript
// app/Jobs/Queue/Batch/Crawl.ts
const queueConfig = {
redis: {
port: process.env.REDIS_PORT,
host: process.env.REDIS_HOST,
password: process.env.REDIS_PASS,
tls: true,
db: 2
}
}
const queue = new Queue.default('link-batches', queueConfig as any);Another file then imports this function and runs it through an isolated worker:
typescript
// app/Jobs/Queue/Batch/Crawl.ts
import Crawl, { add, MAX_ATTEMPTS, MAX_CONCURRENCY } from '#app/Services/BullQueue/Batch/Crawl.js'
import { onBatchError, onJobStalled } from "#app/Services/Batch/SlackNotifications.js";
import '#app/Services/BullQueue/Batch/CrawlTicker.js';
const { appPath } = await import("#app/Core/Paths.js");
const db = await getDb();
if (!global.bull_queue_process_set) {
Crawl.process('__default__', MAX_CONCURRENCY, appPath('Services/BullQueue/Batch/CrawlProcess.cjs'))
Crawl
.on('failed', async (job, error) => {
if (await canRequeueJob(job)) return requeueJob(job);
await setBatchError('Error caught by bull: ' + error.toString(), job);
})
.on('stalled', async (job) => {
if (await canRequeueJob(job)) return requeueJob(job);
await setBatchError('Job stalled', job);
})
global.bull_queue_process_set = true;
}This is done this way as any file that calls the isolated worker file will start another instance of the queue. Therefore it is extremely important that no file imports the worker file except its invoker (this being pm2 or kubernetes).
Cron
At the present time the application does not make use of its cron capabilities. However should the need arise, this is easily configurable and enabled. Behind the scenes and isolated worker is run that loads the cron files and periodically runs the functions defined in them as defined in the class.
All cron workers are found and placed in the App/Jobs/Cron folder.
In order for cron watcher to run, the app/Core/Loaders/Cron.js needs to be executed. The app/start script will automatically run this.
An example of a cron definition can be found below.
javascript
export default class extends Base {
constructor() {
super();
// Cron schedule
this.setSchedule('0 0 * * *')
// Enables / Disables the worker
this.shouldRun(false)
}
async handler() {
// Do something
}
}Current Workers
Below is a list of the main workers in Trieste and what their functions are.
| Worker | type | Description |
|---|---|---|
| app/Jobs/Queue/Batch/Crawl.ts | Queue | BullMQ worker that fetches and filters serp results when a batch is created |
| app/Jobs/Queue/Email/Crawl.ts | Queue | BullMQ worker that fetches incoming emails from all active and authenticated email accounts |
| app/Jobs/Queue/LinkMonitoring/Listener.ts | Isolated Worker | Worker that uses playwright to check target links in leads to make sure they have the required anchor url and link |
| app/Jobs/Pm2/CheckEmailAccounts.js | Isolated Worker | Worker that checks if email accounts are able to send/receive emails (once a user submits them for testing) |
API Routes
The application has over 200 active routes with varying complexity. For a better developer experience and make these easily identifiable, a routing mechanism was built on top of fastify.
This allows for a variety of advantages such as allowing routes to be easily grouped, reused, and share middleware logic.
All routes can be found here: app/Routes/api.ts
Below is an example of a route group definition:
typescript
Router.group('/links', [
/*
* CRUD and index routes for the links controller. This generates the following routes:
* GET /links -> Links.index()
* GET /links/:id -> Links.get()
* POST /links -> Links.create()
* PUT /links/:id -> Links.update()
*/
Router.crudAndIndex('/', Links, {id: 'linkId'}),
// /links/bulk-status/counter -> LinksBulkStatus.counter()
Router.get('/bulk-status/counter', LinksBulkStatus, LinksBulkStatus.prototype.counter),
// /links/mass-update -> Links.massUpdate()
Router.put('/mass-update', Links, Links.prototype.massUpdate),
Router.group('/:linkId', [
/**
* Crud and index for contacts routes. This generates the following routes
* GET /links/:linkId/contacts -> LinksContacts.index()
* GET /links/:linkId/contacts/:contactId -> LinksContacts.get()
* POST /links/:linkId/contacts -> LinksContacts.create()
* PUT /links/:linkId/contacts/:contactId -> LinksContacts.update()
*/
Router.crudAndIndex('/contacts', LinksContacts, {id: 'contactId'}),
// /links/:linkId/content -> LinksContent.create()
Router.post('/content', LinksContent, LinksContent.prototype.create),
// /links/:linkId/content -> LinksContent.index()
Router.get('/content', LinksContent, LinksContent.prototype.index),
// /links/:linkId/emails -> LinksEmails.index()
Router.get('/emails', LinksEmails, LinksEmails.prototype.index),
// /links/:linkId/sync-hunter-emails -> Links.sync()
Router.post('/sync-hunter-emails', Links, Links.prototype.sync),
// /links/:linkId/metrics -> LinksMetrics.index()
Router.get('/metrics', LinksMetrics, LinksMetrics.prototype.index),
// /links/:linkId/monitor -> LinksMonitor.get()
Router.get('/monitor', LinksMonitor, LinksMonitor.prototype.get),
// /links/:linkId/move -> Links.move()
Router.post('/move', Links, Links.prototype.move),
// links/:linkId/compare-stats -> Links.compareStats()
Router.get('/compare-stats', Links, Links.prototype.compareStats),
// links/:linkId/refresh-stats -> Links.refreshStats()
Router.get('/refresh-stats', Links, Links.prototype.refreshStats),
// links/:linkId/relationship -> LinksRelationship.index()
Router.get('/relationship', LinksRelationship, LinksRelationship.prototype.index),
])
]).middleware('auth') // Forces all routes in this group to pass the auth middle found in app/Controllers/Middlware/Auth.tsAll available routes
While it is best to investigate the code and see the logic under each route, below is a list of all the available routes.
| Route | Type | Description |
|---|---|---|
| /v1/api/check | GET | Checks the api is working |
| /v1/auth/check | GET | Checks that the user is authenticated |
| /v1/auth/forgotten-password | POST | Request a new password email |
| /v1/auth/login | POST | Login request |
| /v1/auth/refresh-token | POST | Refresh token request |
| /v1/auth/reset-password | POST | Reset user password using token received from email |
| /v1/auth/store-timer | POST | (old) stores user engagement time per page |
| /v1/admin/:resource | POST | Used for admin section to manage resources |
| /v1/admin/:resource/index | GET | Used for admin section to manage resources |
| /v1/admin/:resource/:resourceId | DELETE | Used for admin section to manage resources |
| /v1/admin/:resource/:resourceId | GET | Used for admin section to manage resources |
| /v1/admin/:resource/:resourceId | PUT | Used for admin section to manage resources |
| /v1/admin/:resource/:resourceId/mass-through-update | POST | Used for admin section to manage resources |
| /v1/companies/:companyId | GET | Get a company record |
| /v1/companies/:companyId | DELETE | Delete a company record |
| /v1/companies/:companyId | PUT | Update a company record |
| /v1/companies | POST | Create a company record |
| /v1/companies | GET | Get a list of companies |
| /v1/companies/:companyId/batches/:batchId | GET | Get a batch |
| /v1/companies/:companyId/batches/:batchId | DELETE | Delete a batch |
| /v1/companies/:companyId/batches/:batchId | PUT | Update a batch |
| /v1/companies/:companyId/batches | POST | Create a batch |
| /v1/companies/:companyId/batches | GET | Get a batch |
| /v1/companies/:companyId/batches/start | POST | Restart multiple batches |
| /v1/companies/:companyId/batches/:batchId/dedupe | POST | Remove duplicate links from a batch |
| /v1/companies/:companyId/batches/:batchId/link/mass-update | POST | Mass update link state in a batch |
| /v1/companies/:companyId/batches/:batchId/manual-lead | POST | Create a manual link in a batch |
| /v1/companies/:companyId/batches/:batchId/reapply-filters | POST | Re-run batch filters |
| /v1/companies/:companyId/batches/:batchId/start | POST | Restart a batch |
| /v1/companies/:companyId/billing/total | GET | Get total money value of billable links |
| /v1/companies/:companyId/blacklisted-contacts | DELETE | Delete a number blacklisted contact |
| /v1/companies/:companyId/blacklisted-contacts | POST | Create a blacklisted contact |
| /v1/companies/:companyId/blacklisted-contacts | GET | Get a list of blacklisted contacts |
| /v1/companies/:companyId/blacklisted-contacts/bulk-create | POST | Create multiple blacklisted contacts |
| /v1/companies/:companyId/blacklisted-contacts/:blacklistId | GET | Get a blacklisted contact |
| /v1/companies/:companyId/blacklisted-contacts/:blacklistId | DELETE | Delete a blacklisted contact |
| /v1/companies/:companyId/blacklisted-contacts/:blacklistId | PUT | Updated a blacklisted contact |
| /v1/companies/:companyId/blacklisted-domains/:blacklistId | GET | Get a blacklisted domain |
| /v1/companies/:companyId/blacklisted-domains/:blacklistId | DELETE | Delete a blacklisted domain |
| /v1/companies/:companyId/blacklisted-domains/:blacklistId | PUT | Updated a blacklisted domain |
| /v1/companies/:companyId/blacklisted-domains | POST | Create a blacklisted domain |
| /v1/companies/:companyId/blacklisted-domains | GET | Get a list of blacklisted domain |
| /v1/companies/:companyId/blacklisted-domains/bulk-create | POST | Create multiple blacklisted domain |
| /v1/companies/:companyId/unsubscribe-email | POST | Blacklist a email account |
| /v1/companies/:companyId/campaigns/:campaignId | GET | Get a campaign |
| /v1/companies/:companyId/campaigns/:campaignId | DELETE | Delete a campaign |
| /v1/companies/:companyId/campaigns/:campaignId | PUT | Update a campaign |
| /v1/companies/:companyId/campaigns | POST | Create a campaign |
| /v1/companies/:companyId/campaigns | GET | Get a list of campaigns |
| /v1/companies/:companyId/clients/:clientId | GET | Get a clients |
| /v1/companies/:companyId/clients/:clientId | DELETE | Delete a clients |
| /v1/companies/:companyId/clients/:clientId | PUT | Update a clients |
| /v1/companies/:companyId/clients | POST | Create a client |
| /v1/companies/:companyId/clients | GET | Get a list of clients |
| /v1/companies/:companyId/content/:contentId | GET | Get a content item |
| /v1/companies/:companyId/content/:contentId | DELETE | Delete a content item |
| /v1/companies/:companyId/content/:contentId | PUT | Update a content |
| /v1/companies/:companyId/content | POST | Create a content item |
| /v1/companies/:companyId/content | GET | Get a list of content |
| /v1/companies/:companyId/content/:contentId/attachments/:attachmentId | GET | Get a content items attachments |
| /v1/companies/:companyId/content/:contentId/attachments/:attachmentId | DELETE | Delete a content items attachments |
| /v1/companies/:companyId/content/:contentId/attachments/:attachmentId | PUT | Update a content items attachments |
| /v1/companies/:companyId/content/:contentId/attachments | POST | Create a list of content item attachments |
| /v1/companies/:companyId/content/:contentId/attachments | GET | Get a list of content item attachments |
| /v1/companies/:companyId/content/:contentId/history/:historyId | GET | Get a content item history |
| /v1/companies/:companyId/content/:contentId/history/:historyId | DELETE | Delete a content item history |
| /v1/companies/:companyId/content/:contentId/history/:historyId | PUT | Update a content item history |
| /v1/companies/:companyId/content/:contentId/history | POST | Create a content history |
| /v1/companies/:companyId/content/:contentId/history | GET | Get a list of content history |
| /v1/companies/:companyId/content/:contentId/attach | POST | Create a list of content attachments |
| /v1/companies/:companyId/content/:contentId/detach | POST | Create a list of content detachments |
| /v1/companies/:companyId/content/counts | GET | Get a content item word count |
| /v1/companies/:companyId/dashboard/content-writer/content | GET | content dashboard reports |
| /v1/companies/:companyId/dashboard/link-builder-tasks | GET | linkbuilder dashboard report |
| /v1/companies/:companyId/dashboard/sites | GET | sites dashboard report |
| /v1/companies/:companyId/dashboard/linkdev/email-accounts | GET | email accounts dashboard report |
| /v1/companies/:companyId/dashboard/linkdev/leads | GET | linkdev leads dashboard report |
| /v1/companies/:companyId/dashboard/linkdev/progress | GET | linkdev progress dashboard report |
| /v1/companies/:companyId/dashboard/linkdev/unread-emails | GET | unread emails dashboard report |
| /v1/companies/:companyId/preview-email | POST | Preview of email content after replacements |
| /v1/companies/:companyId/resend-email | POST | Re-send a previously send email |
| /v1/companies/:companyId/send-email | POST | Send a email |
| /v1/companies/:companyId/store-contact-form | POST | store a contact form email |
| /v1/companies/:companyId/email-brands/:id | GET | Get a email brand |
| /v1/companies/:companyId/email-brands/:id | DELETE | Delete a email brand |
| /v1/companies/:companyId/email-brands/:id | PUT | Update a email brand |
| /v1/companies/:companyId/email-brands | POST | Create a email brand |
| /v1/companies/:companyId/email-brands | GET | Get a list of email brands |
| /v1/companies/:companyId/email-templates/:templateId | GET | Get a email templates |
| /v1/companies/:companyId/email-templates/:templateId | DELETE | Delete a email templates |
| /v1/companies/:companyId/email-templates/:templateId | PUT | Update a email templates |
| /v1/companies/:companyId/email-templates | POST | Create a email templates |
| /v1/companies/:companyId/email-templates | GET | Get a list of email templates |
| /v1/companies/:companyId/invoice | POST | Create a billable invoice |
| /v1/companies/:companyId/email-accounts/:accountId | GET | Get a email account |
| /v1/companies/:companyId/email-accounts/:accountId | DELETE | Delete a email account |
| /v1/companies/:companyId/email-accounts/:accountId | PUT | Update a email account |
| /v1/companies/:companyId/email-accounts | POST | Create a email accounts |
| /v1/companies/:companyId/email-accounts | GET | Get a list of email accounts |
| /v1/companies/:companyId/email-accounts/test | POST | Test a email accounts readability and sendability |
| /v1/companies/:companyId/email-accounts/test-status | GET | Get testing status across all emails |
| /v1/companies/:companyId/email-accounts/users | GET | Get a list of email accounts users |
| /v1/companies/:companyId/email-accounts/:accountId/users/:userAccountId | GET | Get a email accounts user |
| /v1/companies/:companyId/email-accounts/:accountId/users/:userAccountId | DELETE | Delete a email accounts user |
| /v1/companies/:companyId/email-accounts/:accountId/users/:userAccountId | PUT | Update a email accounts user |
| /v1/companies/:companyId/email-accounts/:accountId/users | POST | Create a email accounts user |
| /v1/companies/:companyId/email-accounts/:accountId/users | GET | Get a list of email accounts users |
| /v1/companies/:companyId/email-accounts/:accountId/reset-error | PUT | Reset a email accounts error |
| /v1/companies/:companyId/email-accounts/:accountId/emails/:emailId | GET | Get a email accounts email |
| /v1/companies/:companyId/email-accounts/:accountId/emails/:emailId | DELETE | Delete a email accounts email |
| /v1/companies/:companyId/email-accounts/:accountId/emails/:emailId | PUT | Update a email accounts email |
| /v1/companies/:companyId/email-accounts/:accountId/emails | POST | Create a email accounts email |
| /v1/companies/:companyId/email-accounts/:accountId/emails | GET | Get a list of email accounts emails |
| /v1/companies/:companyId/email-accounts/:accountId/emails/:emailId/download | GET | Download attachment from an email |
| /v1/companies/:companyId/email-accounts/:accountId/emails/:emailId/read | PUT | Mark email as read |
| /v1/companies/:companyId/email-accounts/:accountId/emails/bulk-move | PUT | Bulk move a email |
| /v1/companies/:companyId/links/:linkId | GET | Get a link |
| /v1/companies/:companyId/links/:linkId | DELETE | Delete a link |
| /v1/companies/:companyId/links/:linkId | PUT | Update a link |
| /v1/companies/:companyId/links | POST | Create a link |
| /v1/companies/:companyId/links | GET | Get a list of links |
| /v1/companies/:companyId/links/bulk-status/counter | GET | Get a count of effectable links |
| /v1/companies/:companyId/links/mass-update | PUT | mass update links |
| /v1/companies/:companyId/links/:linkId/contacts/:contactId | GET | Get a links contact |
| /v1/companies/:companyId/links/:linkId/contacts/:contactId | DELETE | Delete a links contact |
| /v1/companies/:companyId/links/:linkId/contacts/:contactId | PUT | Update a links contact |
| /v1/companies/:companyId/links/:linkId/contacts | POST | Create a links contacts |
| /v1/companies/:companyId/links/:linkId/contacts | GET | Get a list of links contacts |
| /v1/companies/:companyId/links/:linkId/content | POST | Create a list of links content |
| /v1/companies/:companyId/links/:linkId/content | GET | Get a list of links content |
| /v1/companies/:companyId/links/:linkId/emails | GET | Get a list of links emails |
| /v1/companies/:companyId/links/:linkId/sync-hunter-emails | POST | Get email contacts from hunter IO |
| /v1/companies/:companyId/links/:linkId/metrics | GET | Get a links metrics score |
| /v1/companies/:companyId/links/:linkId/monitor | GET | Get a links monitoring states |
| /v1/companies/:companyId/links/:linkId/move | POST | Move a link |
| /v1/companies/:companyId/links/:linkId/compare-stats | GET | Compare a links historic metric stats |
| /v1/companies/:companyId/links/:linkId/refresh-stats | GET | Refresh the links metrics |
| /v1/companies/:companyId/links/:linkId/relationship | GET | Get a list of links relationship |
| /v1/companies/:companyId/projects/:projectId | GET | Get a projects |
| /v1/companies/:companyId/projects/:projectId | DELETE | Delete a projects |
| /v1/companies/:companyId/projects/:projectId | PUT | Update a projects |
| /v1/companies/:companyId/projects | POST | Create a projects |
| /v1/companies/:companyId/projects | GET | Get a list of projects |
| /v1/companies/:companyId/notes/:noteId | GET | Get a notes |
| /v1/companies/:companyId/notes/:noteId | DELETE | Delete a notes |
| /v1/companies/:companyId/notes/:noteId | PUT | Update a notes |
| /v1/companies/:companyId/notes | POST | Create a of notes |
| /v1/companies/:companyId/notes | GET | Get a list of notes |
| /v1/companies/:companyId/note-templates/:noteId | GET | Get a note template |
| /v1/companies/:companyId/note-templates/:noteId | DELETE | Delete a note template |
| /v1/companies/:companyId/note-templates/:noteId | PUT | Update a note template |
| /v1/companies/:companyId/note-templates | POST | Create a list of note template |
| /v1/companies/:companyId/note-templates | GET | Get a note template |
| /v1/companies/:companyId/outreach-groups/:groupId | GET | Get a outreach groups |
| /v1/companies/:companyId/outreach-groups/:groupId | DELETE | Delete a outreach groups |
| /v1/companies/:companyId/outreach-groups/:groupId | PUT | Update a outreach groups |
| /v1/companies/:companyId/outreach-groups | POST | Create a outreach groups |
| /v1/companies/:companyId/outreach-groups | GET | Get a list of outreach groups |
| /v1/companies/:companyId/stop-words/:stopWordId | GET | Get a stop words |
| /v1/companies/:companyId/stop-words/:stopWordId | DELETE | Delete a stop words |
| /v1/companies/:companyId/stop-words/:stopWordId | PUT | Update a stop words |
| /v1/companies/:companyId/stop-words | POST | Create a list of stop words |
| /v1/companies/:companyId/stop-words | GET | Get a stop words |
| /v1/companies/:companyId/stop-words/bulk-create | POST | Create a list of stop words bulk create |
| /v1/companies/:companyId/stop-words/bulk-delete | POST | Create a list of stop words bulk delete |
| /v1/companies/:companyId/publisher-history | GET | Get pubisher history data |
| /v1/companies/:companyId/search | GET | Search |
| /v1/companies/:companyId/sites/:siteId | GET | Get a site |
| /v1/companies/:companyId/sites/:siteId | DELETE | Delete a site |
| /v1/companies/:companyId/sites/:siteId | PUT | Update a site |
| /v1/companies/:companyId/sites | POST | Create a of site |
| /v1/companies/:companyId/sites | GET | Get a list of sites |
| /v1/companies/:companyId/sites/:siteId/blacklisted-domains/:blacklistId | GET | Get a sites blacklisted domain |
| /v1/companies/:companyId/sites/:siteId/blacklisted-domains/:blacklistId | DELETE | Delete a sites blacklisted domain |
| /v1/companies/:companyId/sites/:siteId/blacklisted-domains/:blacklistId | PUT | Update a sites blacklisted domain |
| /v1/companies/:companyId/sites/:siteId/blacklisted-domains | POST | Create a sites blacklisted domain |
| /v1/companies/:companyId/sites/:siteId/blacklisted-domains | GET | Get a list of sites blacklisted domain |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId | GET | Get a sites target |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId | DELETE | Delete a sites target |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId | PUT | Update a sites target |
| /v1/companies/:companyId/sites/:siteId/targets | POST | Create a sites target |
| /v1/companies/:companyId/sites/:siteId/targets | GET | Get a list of sites targets |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId/content/:contentId | GET | Get a sites targets content |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId/content/:contentId | DELETE | Delete a sites targets content |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId/content/:contentId | PUT | Update a sites targets content |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId/content | POST | Create a sites targets content |
| /v1/companies/:companyId/sites/:siteId/targets/:targetId/content | GET | Get a list of sites targets content |
| /v1/companies/:companyId/sites/:siteId/clone | POST | Clone a site |
| /v1/companies/:companyId/sites/:siteId/copy | POST | Copy a site |
| /v1/companies/:companyId/sites/:siteId/emails | GET | Get emails belonging to a site |
| /v1/companies/:companyId/sites/:siteId/manual-lead | POST | Create a manual lead for a site |
| /v1/companies/:companyId/sites/:siteId/stop-words | GET | Get all blocked words for a site |
| /v1/companies/:companyId/sites/:siteId/email-accounts/bulkAssign | POST | bulk assign email accounts to a site |
| /v1/companies/:companyId/sites/:siteId/templates/bulkAssign | POST | Bulk assign templats to a site |
| /v1/companies/:companyId/targets | GET | Get a list of targets |
| /v1/companies/:companyId/users/:userId | GET | Get a user |
| /v1/companies/:companyId/users/:userId | DELETE | Delete a user |
| /v1/companies/:companyId/users/:userId | PUT | Update a user |
| /v1/companies/:companyId/users | POST | Create a of users |
| /v1/companies/:companyId/users | GET | Get a list of users |
| /v1/companies/:companyId/users/:userId/email-accounts | GET | Get a list of users email accounts |
| /v1/companies/:companyId/users/:userId/email-accounts | POST | Assign a users email accounts |
| /v1/companies/:companyId/users/:userId/email-accounts/:accountId | DELETE | Delete a users email accounts |
| /v1/companies/:companyId/users/:userId/email-accounts/mass-assign | POST | mass assign email accounts to a user |
| /v1/companies/:companyId/users/:userId/emails | GET | Get a list of users emails |
| /v1/companies/:companyId/user/email-accounts | GET | Get a list of user email accounts |
| /v1/companies/:companyId/user-groups/:groupId | GET | Get a user group |
| /v1/companies/:companyId/user-groups/:groupId | DELETE | Delete a user group |
| /v1/companies/:companyId/user-groups/:groupId | PUT | Update a user group |
| /v1/companies/:companyId/user-groups | POST | Create a user group |
| /v1/companies/:companyId/user-groups | GET | Get a list of user groups |
| /v1/user | GET | Get a list of v1 user |
| /v1/user | PUT | Update a list of v1 user |
| /v1/user/alerts | GET | Get user alerts |
| /v1/user/alerts/clear-all | POST | Clear user alerts |
| /v1/user/alerts/count | GET | Get a list of v1 user alerts count |
| /v1/user/alerts/table-order | PUT | Update a list of v1 user alerts table order |
| /v1/user/dashboard/emails | GET | Get user dashboard reports |
| /v1/user/dashboard/site-stats | GET | Get user dashboard site stats |
| /v1/user/last-viewed-company | POST | Get the users last viewed companies |
| /v1/proxy | GET | Proxy a url |
| /v1/proxy/link/external | POST | Proxy an external url |
3rd Party Services
API Services
- Ahrefs - used for metrics
- Majestics - used for metrics
- Semrush - used for metrics
- Data4SEO - used for Serp results
- Ayima cache - https://github.com/Ayima/api-cache
Google
The google account integration in Trieste is tricky to say the least. In order to send and receive emails from a Gmail account they need to be authenticated through a Google App. Due to Google’s terms and verification process, we have been able to get a verified app in a cost effective manner.
If a Google app is not verified, it has a hard limit of 100 users that can be authenticated to this. So in order to bypass this, multiple google apps have been set up in order to rotate across Trieste different accounts. The gmail accounts used for each individual ‘app’ can be found before. The credentials of each of these accounts are stored in the Ayima 1Pass system under the dev folder.
- kierenchambersothertest@gmail.com - charizard.adwisemedia.co
- donthavesexwithsharks@gmail.com - greninja.adwisemedia.co
- advicemedia.arceus@gmail.com - mewtwo.adwisemedia.co
- iambenphilips@gmail.com - arceus.adwisemedia.co
Once logged in to any of these accounts, you are able to view the app here: