Skip to content

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: regcred

Queue

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.

WorkertypeDescription
app/Jobs/Queue/Batch/Crawl.tsQueueBullMQ worker that fetches and filters serp results when a batch is created
app/Jobs/Queue/Email/Crawl.tsQueueBullMQ worker that fetches incoming emails from all active and authenticated email accounts
app/Jobs/Queue/LinkMonitoring/Listener.tsIsolated WorkerWorker that uses playwright to check target links in leads to make sure they have the required anchor url and link
app/Jobs/Pm2/CheckEmailAccounts.jsIsolated WorkerWorker 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.ts

All 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.

RouteTypeDescription
/v1/api/checkGETChecks the api is working
/v1/auth/checkGETChecks that the user is authenticated
/v1/auth/forgotten-passwordPOSTRequest a new password email
/v1/auth/loginPOSTLogin request
/v1/auth/refresh-tokenPOSTRefresh token request
/v1/auth/reset-passwordPOSTReset user password using token received from email
/v1/auth/store-timerPOST(old) stores user engagement time per page
/v1/admin/:resourcePOSTUsed for admin section to manage resources
/v1/admin/:resource/indexGETUsed for admin section to manage resources
/v1/admin/:resource/:resourceIdDELETEUsed for admin section to manage resources
/v1/admin/:resource/:resourceIdGETUsed for admin section to manage resources
/v1/admin/:resource/:resourceIdPUTUsed for admin section to manage resources
/v1/admin/:resource/:resourceId/mass-through-updatePOSTUsed for admin section to manage resources
/v1/companies/:companyIdGETGet a company record
/v1/companies/:companyIdDELETEDelete a company record
/v1/companies/:companyIdPUTUpdate a company record
/v1/companiesPOSTCreate a company record
/v1/companiesGETGet a list of companies
/v1/companies/:companyId/batches/:batchIdGETGet a batch
/v1/companies/:companyId/batches/:batchIdDELETEDelete a batch
/v1/companies/:companyId/batches/:batchIdPUTUpdate a batch
/v1/companies/:companyId/batchesPOSTCreate a batch
/v1/companies/:companyId/batchesGETGet a batch
/v1/companies/:companyId/batches/startPOSTRestart multiple batches
/v1/companies/:companyId/batches/:batchId/dedupePOSTRemove duplicate links from a batch
/v1/companies/:companyId/batches/:batchId/link/mass-updatePOSTMass update link state in a batch
/v1/companies/:companyId/batches/:batchId/manual-leadPOSTCreate a manual link in a batch
/v1/companies/:companyId/batches/:batchId/reapply-filtersPOSTRe-run batch filters
/v1/companies/:companyId/batches/:batchId/startPOSTRestart a batch
/v1/companies/:companyId/billing/totalGETGet total money value of billable links
/v1/companies/:companyId/blacklisted-contactsDELETEDelete a number blacklisted contact
/v1/companies/:companyId/blacklisted-contactsPOSTCreate a blacklisted contact
/v1/companies/:companyId/blacklisted-contactsGETGet a list of blacklisted contacts
/v1/companies/:companyId/blacklisted-contacts/bulk-createPOSTCreate multiple blacklisted contacts
/v1/companies/:companyId/blacklisted-contacts/:blacklistIdGETGet a blacklisted contact
/v1/companies/:companyId/blacklisted-contacts/:blacklistIdDELETEDelete a blacklisted contact
/v1/companies/:companyId/blacklisted-contacts/:blacklistIdPUTUpdated a blacklisted contact
/v1/companies/:companyId/blacklisted-domains/:blacklistIdGETGet a blacklisted domain
/v1/companies/:companyId/blacklisted-domains/:blacklistIdDELETEDelete a blacklisted domain
/v1/companies/:companyId/blacklisted-domains/:blacklistIdPUTUpdated a blacklisted domain
/v1/companies/:companyId/blacklisted-domainsPOSTCreate a blacklisted domain
/v1/companies/:companyId/blacklisted-domainsGETGet a list of blacklisted domain
/v1/companies/:companyId/blacklisted-domains/bulk-createPOSTCreate multiple blacklisted domain
/v1/companies/:companyId/unsubscribe-emailPOSTBlacklist a email account
/v1/companies/:companyId/campaigns/:campaignIdGETGet a campaign
/v1/companies/:companyId/campaigns/:campaignIdDELETEDelete a campaign
/v1/companies/:companyId/campaigns/:campaignIdPUTUpdate a campaign
/v1/companies/:companyId/campaignsPOSTCreate a campaign
/v1/companies/:companyId/campaignsGETGet a list of campaigns
/v1/companies/:companyId/clients/:clientIdGETGet a clients
/v1/companies/:companyId/clients/:clientIdDELETEDelete a clients
/v1/companies/:companyId/clients/:clientIdPUTUpdate a clients
/v1/companies/:companyId/clientsPOSTCreate a client
/v1/companies/:companyId/clientsGETGet a list of clients
/v1/companies/:companyId/content/:contentIdGETGet a content item
/v1/companies/:companyId/content/:contentIdDELETEDelete a content item
/v1/companies/:companyId/content/:contentIdPUTUpdate a content
/v1/companies/:companyId/contentPOSTCreate a content item
/v1/companies/:companyId/contentGETGet a list of content
/v1/companies/:companyId/content/:contentId/attachments/:attachmentIdGETGet a content items attachments
/v1/companies/:companyId/content/:contentId/attachments/:attachmentIdDELETEDelete a content items attachments
/v1/companies/:companyId/content/:contentId/attachments/:attachmentIdPUTUpdate a content items attachments
/v1/companies/:companyId/content/:contentId/attachmentsPOSTCreate a list of content item attachments
/v1/companies/:companyId/content/:contentId/attachmentsGETGet a list of content item attachments
/v1/companies/:companyId/content/:contentId/history/:historyIdGETGet a content item history
/v1/companies/:companyId/content/:contentId/history/:historyIdDELETEDelete a content item history
/v1/companies/:companyId/content/:contentId/history/:historyIdPUTUpdate a content item history
/v1/companies/:companyId/content/:contentId/historyPOSTCreate a content history
/v1/companies/:companyId/content/:contentId/historyGETGet a list of content history
/v1/companies/:companyId/content/:contentId/attachPOSTCreate a list of content attachments
/v1/companies/:companyId/content/:contentId/detachPOSTCreate a list of content detachments
/v1/companies/:companyId/content/countsGETGet a content item word count
/v1/companies/:companyId/dashboard/content-writer/contentGETcontent dashboard reports
/v1/companies/:companyId/dashboard/link-builder-tasksGETlinkbuilder dashboard report
/v1/companies/:companyId/dashboard/sitesGETsites dashboard report
/v1/companies/:companyId/dashboard/linkdev/email-accountsGETemail accounts dashboard report
/v1/companies/:companyId/dashboard/linkdev/leadsGETlinkdev leads dashboard report
/v1/companies/:companyId/dashboard/linkdev/progressGETlinkdev progress dashboard report
/v1/companies/:companyId/dashboard/linkdev/unread-emailsGETunread emails dashboard report
/v1/companies/:companyId/preview-emailPOSTPreview of email content after replacements
/v1/companies/:companyId/resend-emailPOSTRe-send a previously send email
/v1/companies/:companyId/send-emailPOSTSend a email
/v1/companies/:companyId/store-contact-formPOSTstore a contact form email
/v1/companies/:companyId/email-brands/:idGETGet a email brand
/v1/companies/:companyId/email-brands/:idDELETEDelete a email brand
/v1/companies/:companyId/email-brands/:idPUTUpdate a email brand
/v1/companies/:companyId/email-brandsPOSTCreate a email brand
/v1/companies/:companyId/email-brandsGETGet a list of email brands
/v1/companies/:companyId/email-templates/:templateIdGETGet a email templates
/v1/companies/:companyId/email-templates/:templateIdDELETEDelete a email templates
/v1/companies/:companyId/email-templates/:templateIdPUTUpdate a email templates
/v1/companies/:companyId/email-templatesPOSTCreate a email templates
/v1/companies/:companyId/email-templatesGETGet a list of email templates
/v1/companies/:companyId/invoicePOSTCreate a billable invoice
/v1/companies/:companyId/email-accounts/:accountIdGETGet a email account
/v1/companies/:companyId/email-accounts/:accountIdDELETEDelete a email account
/v1/companies/:companyId/email-accounts/:accountIdPUTUpdate a email account
/v1/companies/:companyId/email-accountsPOSTCreate a email accounts
/v1/companies/:companyId/email-accountsGETGet a list of email accounts
/v1/companies/:companyId/email-accounts/testPOSTTest a email accounts readability and sendability
/v1/companies/:companyId/email-accounts/test-statusGETGet testing status across all emails
/v1/companies/:companyId/email-accounts/usersGETGet a list of email accounts users
/v1/companies/:companyId/email-accounts/:accountId/users/:userAccountIdGETGet a email accounts user
/v1/companies/:companyId/email-accounts/:accountId/users/:userAccountIdDELETEDelete a email accounts user
/v1/companies/:companyId/email-accounts/:accountId/users/:userAccountIdPUTUpdate a email accounts user
/v1/companies/:companyId/email-accounts/:accountId/usersPOSTCreate a email accounts user
/v1/companies/:companyId/email-accounts/:accountId/usersGETGet a list of email accounts users
/v1/companies/:companyId/email-accounts/:accountId/reset-errorPUTReset a email accounts error
/v1/companies/:companyId/email-accounts/:accountId/emails/:emailIdGETGet a email accounts email
/v1/companies/:companyId/email-accounts/:accountId/emails/:emailIdDELETEDelete a email accounts email
/v1/companies/:companyId/email-accounts/:accountId/emails/:emailIdPUTUpdate a email accounts email
/v1/companies/:companyId/email-accounts/:accountId/emailsPOSTCreate a email accounts email
/v1/companies/:companyId/email-accounts/:accountId/emailsGETGet a list of email accounts emails
/v1/companies/:companyId/email-accounts/:accountId/emails/:emailId/downloadGETDownload attachment from an email
/v1/companies/:companyId/email-accounts/:accountId/emails/:emailId/readPUTMark email as read
/v1/companies/:companyId/email-accounts/:accountId/emails/bulk-movePUTBulk move a email
/v1/companies/:companyId/links/:linkIdGETGet a link
/v1/companies/:companyId/links/:linkIdDELETEDelete a link
/v1/companies/:companyId/links/:linkIdPUTUpdate a link
/v1/companies/:companyId/linksPOSTCreate a link
/v1/companies/:companyId/linksGETGet a list of links
/v1/companies/:companyId/links/bulk-status/counterGETGet a count of effectable links
/v1/companies/:companyId/links/mass-updatePUTmass update links
/v1/companies/:companyId/links/:linkId/contacts/:contactIdGETGet a links contact
/v1/companies/:companyId/links/:linkId/contacts/:contactIdDELETEDelete a links contact
/v1/companies/:companyId/links/:linkId/contacts/:contactIdPUTUpdate a links contact
/v1/companies/:companyId/links/:linkId/contactsPOSTCreate a links contacts
/v1/companies/:companyId/links/:linkId/contactsGETGet a list of links contacts
/v1/companies/:companyId/links/:linkId/contentPOSTCreate a list of links content
/v1/companies/:companyId/links/:linkId/contentGETGet a list of links content
/v1/companies/:companyId/links/:linkId/emailsGETGet a list of links emails
/v1/companies/:companyId/links/:linkId/sync-hunter-emailsPOSTGet email contacts from hunter IO
/v1/companies/:companyId/links/:linkId/metricsGETGet a links metrics score
/v1/companies/:companyId/links/:linkId/monitorGETGet a links monitoring states
/v1/companies/:companyId/links/:linkId/movePOSTMove a link
/v1/companies/:companyId/links/:linkId/compare-statsGETCompare a links historic metric stats
/v1/companies/:companyId/links/:linkId/refresh-statsGETRefresh the links metrics
/v1/companies/:companyId/links/:linkId/relationshipGETGet a list of links relationship
/v1/companies/:companyId/projects/:projectIdGETGet a projects
/v1/companies/:companyId/projects/:projectIdDELETEDelete a projects
/v1/companies/:companyId/projects/:projectIdPUTUpdate a projects
/v1/companies/:companyId/projectsPOSTCreate a projects
/v1/companies/:companyId/projectsGETGet a list of projects
/v1/companies/:companyId/notes/:noteIdGETGet a notes
/v1/companies/:companyId/notes/:noteIdDELETEDelete a notes
/v1/companies/:companyId/notes/:noteIdPUTUpdate a notes
/v1/companies/:companyId/notesPOSTCreate a of notes
/v1/companies/:companyId/notesGETGet a list of notes
/v1/companies/:companyId/note-templates/:noteIdGETGet a note template
/v1/companies/:companyId/note-templates/:noteIdDELETEDelete a note template
/v1/companies/:companyId/note-templates/:noteIdPUTUpdate a note template
/v1/companies/:companyId/note-templatesPOSTCreate a list of note template
/v1/companies/:companyId/note-templatesGETGet a note template
/v1/companies/:companyId/outreach-groups/:groupIdGETGet a outreach groups
/v1/companies/:companyId/outreach-groups/:groupIdDELETEDelete a outreach groups
/v1/companies/:companyId/outreach-groups/:groupIdPUTUpdate a outreach groups
/v1/companies/:companyId/outreach-groupsPOSTCreate a outreach groups
/v1/companies/:companyId/outreach-groupsGETGet a list of outreach groups
/v1/companies/:companyId/stop-words/:stopWordIdGETGet a stop words
/v1/companies/:companyId/stop-words/:stopWordIdDELETEDelete a stop words
/v1/companies/:companyId/stop-words/:stopWordIdPUTUpdate a stop words
/v1/companies/:companyId/stop-wordsPOSTCreate a list of stop words
/v1/companies/:companyId/stop-wordsGETGet a stop words
/v1/companies/:companyId/stop-words/bulk-createPOSTCreate a list of stop words bulk create
/v1/companies/:companyId/stop-words/bulk-deletePOSTCreate a list of stop words bulk delete
/v1/companies/:companyId/publisher-historyGETGet pubisher history data
/v1/companies/:companyId/searchGETSearch
/v1/companies/:companyId/sites/:siteIdGETGet a site
/v1/companies/:companyId/sites/:siteIdDELETEDelete a site
/v1/companies/:companyId/sites/:siteIdPUTUpdate a site
/v1/companies/:companyId/sitesPOSTCreate a of site
/v1/companies/:companyId/sitesGETGet a list of sites
/v1/companies/:companyId/sites/:siteId/blacklisted-domains/:blacklistIdGETGet a sites blacklisted domain
/v1/companies/:companyId/sites/:siteId/blacklisted-domains/:blacklistIdDELETEDelete a sites blacklisted domain
/v1/companies/:companyId/sites/:siteId/blacklisted-domains/:blacklistIdPUTUpdate a sites blacklisted domain
/v1/companies/:companyId/sites/:siteId/blacklisted-domainsPOSTCreate a sites blacklisted domain
/v1/companies/:companyId/sites/:siteId/blacklisted-domainsGETGet a list of sites blacklisted domain
/v1/companies/:companyId/sites/:siteId/targets/:targetIdGETGet a sites target
/v1/companies/:companyId/sites/:siteId/targets/:targetIdDELETEDelete a sites target
/v1/companies/:companyId/sites/:siteId/targets/:targetIdPUTUpdate a sites target
/v1/companies/:companyId/sites/:siteId/targetsPOSTCreate a sites target
/v1/companies/:companyId/sites/:siteId/targetsGETGet a list of sites targets
/v1/companies/:companyId/sites/:siteId/targets/:targetId/content/:contentIdGETGet a sites targets content
/v1/companies/:companyId/sites/:siteId/targets/:targetId/content/:contentIdDELETEDelete a sites targets content
/v1/companies/:companyId/sites/:siteId/targets/:targetId/content/:contentIdPUTUpdate a sites targets content
/v1/companies/:companyId/sites/:siteId/targets/:targetId/contentPOSTCreate a sites targets content
/v1/companies/:companyId/sites/:siteId/targets/:targetId/contentGETGet a list of sites targets content
/v1/companies/:companyId/sites/:siteId/clonePOSTClone a site
/v1/companies/:companyId/sites/:siteId/copyPOSTCopy a site
/v1/companies/:companyId/sites/:siteId/emailsGETGet emails belonging to a site
/v1/companies/:companyId/sites/:siteId/manual-leadPOSTCreate a manual lead for a site
/v1/companies/:companyId/sites/:siteId/stop-wordsGETGet all blocked words for a site
/v1/companies/:companyId/sites/:siteId/email-accounts/bulkAssignPOSTbulk assign email accounts to a site
/v1/companies/:companyId/sites/:siteId/templates/bulkAssignPOSTBulk assign templats to a site
/v1/companies/:companyId/targetsGETGet a list of targets
/v1/companies/:companyId/users/:userIdGETGet a user
/v1/companies/:companyId/users/:userIdDELETEDelete a user
/v1/companies/:companyId/users/:userIdPUTUpdate a user
/v1/companies/:companyId/usersPOSTCreate a of users
/v1/companies/:companyId/usersGETGet a list of users
/v1/companies/:companyId/users/:userId/email-accountsGETGet a list of users email accounts
/v1/companies/:companyId/users/:userId/email-accountsPOSTAssign a users email accounts
/v1/companies/:companyId/users/:userId/email-accounts/:accountIdDELETEDelete a users email accounts
/v1/companies/:companyId/users/:userId/email-accounts/mass-assignPOSTmass assign email accounts to a user
/v1/companies/:companyId/users/:userId/emailsGETGet a list of users emails
/v1/companies/:companyId/user/email-accountsGETGet a list of user email accounts
/v1/companies/:companyId/user-groups/:groupIdGETGet a user group
/v1/companies/:companyId/user-groups/:groupIdDELETEDelete a user group
/v1/companies/:companyId/user-groups/:groupIdPUTUpdate a user group
/v1/companies/:companyId/user-groupsPOSTCreate a user group
/v1/companies/:companyId/user-groupsGETGet a list of user groups
/v1/userGETGet a list of v1 user
/v1/userPUTUpdate a list of v1 user
/v1/user/alertsGETGet user alerts
/v1/user/alerts/clear-allPOSTClear user alerts
/v1/user/alerts/countGETGet a list of v1 user alerts count
/v1/user/alerts/table-orderPUTUpdate a list of v1 user alerts table order
/v1/user/dashboard/emailsGETGet user dashboard reports
/v1/user/dashboard/site-statsGETGet user dashboard site stats
/v1/user/last-viewed-companyPOSTGet the users last viewed companies
/v1/proxyGETProxy a url
/v1/proxy/link/externalPOSTProxy an external url

3rd Party Services

API Services

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.

Once logged in to any of these accounts, you are able to view the app here:

https://console.cloud.google.com/