Web extensions API services

Using injected API services for developing web extensions.

The web extension has access to services that are injected into the page to facilitate communication with Skedulo’s API.

These services have their appropriate type definitions (for Typescript) defined in the src/Services/Services.ts boilerplate file.

You can use this file to get direct references to everything injected in the web extension.

This page describes the variables and services that are injected in to your web extensions and how to use them.


Skedulo updates the injected services in your environment using environment variables.

As we update the injected services we will also periodically update the boilerplate files.

The following is a list of all variables that are injected into web extensions, and an example of the result.

Name Type Description Example
context string or string[] The reference UID of the context under which the page is loaded referenceUID: "000195c2-1cad-409e-aa22-74181732b2c7"
params [paramName: string]: any Any parameters that have been passed to the web component foo: "bar"
profile Profile Contains information about the user running the web extension roles: ["administrator"]
tenantId: "sk_b30d8a5952d94b47a000d254f2c9256d"
username: "mwheeler@skedulo.com"
credentials Credentials Credentials of the current user and details of API server apiAccessToken: "token"
apiServer: "https://api.skedulo.com"
vendor: {
type: "skedulo",
url: 'https://api.skedulo.com',
token: null
navigation Navigation desc ex

These variables can be used in your Web Extension by importing them and then referencing them (see below example)

import * as React from 'react'
import { Services, profile } from './Services/Services' //import profile variable

interface AppState {
  profile: any

export class App extends React.PureComponent<{}, AppState> {

  constructor(props: {}) {
    this.state = {
      profile: profile //add it to the state

  render() {
    return (
      <div className="App">
       Current User Id: { this.state.profile.userId } //show profile.userId


The below services are exposed to make it easier to call Skedulo’s APIs.

There are some example implementations of these services (for Typescript) defined in the src/Services/DataServices.ts boilerplate file.

You can use these to retrieve and manipulate data and metadata within the Skedulo platform.

Name Description
Services.graphQL This service allows you to send queries to Skedulo’s GraphQL API. This is the primary method of retrieving, consuming and updating data from a web extension. Refer to src/Services/DataServices.ts for usage examples. This also exposes a method for retrieving GraphQL introspection data for any given model if required.
Services.metadata This service allows you to fetch data from our metadata API.
Services.errorClasses This is a map that lists all possible errors that can be thrown while interacting with the available services. For example, a GraphQLExecutionError can occur if there is an error in the defined query. You can use these classes (if needed) to pattern-match (instanceof checks) in Promise catch blocks for easy and contextual error handling.

These services can be used in your Web Extension by importing them and then referencing them (see below example)

import { Services } from './Services' //import the service

export class DataServices {

  constructor(private services: Services) { }

  fetchJobs() {
    return this.services.graphQL //perform a fetch using it
      .fetch<{ jobs: Job[] }>({
        query: JobsQuery
      .then(({ jobs }) => jobs)

//example job interface
export interface Job { 
  UID: string
  Name: string
  Description: string

//example job fetch query
const JobsQuery = `
  jobs {
    edges {
      node {

Example Services.ts file:

interface GraphQLRequest {
  query: string
  variables?: Record<string, any>
  operationName?: string
  context?: Record<string, any>
  extensions?: Record<string, any>

export interface GraphQLMutationResult {
  data: null | { schema: { [operationName: string]: string } }
  errors: null | {
    message: string
    path?: string[]
    locations?: { line: number, column: number }[]

// tslint:disable:no-misused-new
interface GraphQLError {
  getErrors: () => string[]
  new(): GraphQLError

type Model = string

interface IntrospectionField {
  name: string
  type: {
    name: null | 'Instant' | 'Boolean' | 'BigDecimal' | 'String' | Model
    kind: 'SCALAR' | 'NON_NULL' | 'OBJECT' | 'LIST'
    ofType: null | IntrospectionField['type']

interface IntrospectionModelType {
  __type: {
    name: string
    fields: IntrospectionField[]

export interface Vocabulary {
  [schema: string]: {
    [field: string]: {
      value: string,
      label: string

export interface Services {
  graphQL: {
    fetch<T>(operation: GraphQLRequest, endpoint?: string): Promise<T>
    mutate(operation: GraphQLRequest, endpoint?: string): Promise<GraphQLMutationResult>
    fetchMetadataFor(model: string): Promise<IntrospectionModelType>
  metadata: {
    fetchVocabulary(): Promise<Vocabulary>
  errorClasses: {
    GraphQLNetworkError: GraphQLError,
    GraphQLExecutionError: GraphQLError

export interface Profile {
  tenantId: string
  userId: string
  username: string
  roles: string[]

export interface Credentials {
  apiServer: string
  apiAccessToken: string

  vendor: { type: 'skedulo', url: string, token: null } | { type: 'salesforce', url: string, token: string }

export interface Navigation {
  registerRouteHandler: (routeHandler: (routeState: {
    routes: string | string[],
    params: { [paramName: string]: any }
  }) => void) => void
  setParentRoute: (route: string) => void

declare const skedInjected: {
  Services: Services,
  context?: string | string[],
  params: { [paramName: string]: any }
  profile: Profile,
  credentials: Credentials
  navigation: Navigation

export const Services = skedInjected.Services
export const context = skedInjected.context
export const params = skedInjected.params
export const profile = skedInjected.profile
export const credentials = skedInjected.credentials
export const navigation = skedInjected.navigation

Nested types

RealtimeJobChangeSet = Pick<Job, 'UID' | 'JobStatus' | 'Urgency' | 'Locked' | 'Start' | 'Duration' | 'End' | 'Address' | 'GeoLatitude' | 'GeoLongitude'>
RealtimeJobAllocationChangeSet = Pick<IJobAllocation, 'UID' | 'Status' | 'JobId' | 'ResourceId'>
ExtendedRealtimeJobAllocation = RealtimeJobAllocation & { JobId: string, ResourceId: string }

interface IRealtimeChannel {
  getJobStream: () => Observable<RealtimeJobChangeSet[]>,
  getJobAllocationStream: () => Observable<RealtimeJobAllocationChangeSet[]>,
  getUpdatedJobStream: () => Observable<RealtimeJobChangeSet[]>,
  getCreatedJobStream: () => Observable<RealtimeJobChangeSet[]>,
  getUpdatedJobAllocationStream: () => Observable<RealtimeJobAllocationChangeSet[]>,
  getCreatedJobAllocationStream: () => Observable<RealtimeJobAllocationChangeSet[]>

This approach allows you to provide credentials and context that is used to instantiate services for the page and allows you to access the Skedulo GraphQL schema.