NAV
json shell

Advanced employee updater introduction

The advanced employee updater is designed to allow dynamic and customizable employee provisioning and de-provisioning in Expensify. It will allow you to:

API Principles

Employee feed examples with various amounts of information provided:

{
    "Employees": [
        {
            "employeeEmail": "employee@domain.com",
            "managerEmail": "manager@domain.com",
            "policyID": "0123456789ABCDEF",
            "employeeID": "12345"
        },
        {
            "employeeEmail": "manager@domain.com",
            "managerEmail": "ceo@domain.com",
            "policyID": "0123456789ABCDEF",
            "employeeID": "34567"
        }
    ]
}
{
    "Employees": [
        {
            "employeeEmail": "employee@domain.com",
            "managerEmail": "manager@domain.com",
            "policyID": "0123456789ABCDEF",
            "employeeID": "12345",
            "firstName": "John",
            "lastName": "Doe",
            "customField2": "ABC123",
            "approvalLimit": 12300,
            "overLimitApprover": "audit@domain.com",
            "isTerminated": false,
            "additionalPolicyIDs": ["ABCDEF0123456789", "456789ABCDEF0123"]
        },
        {
            "employeeEmail": "manager@domain.com",
            "managerEmail": "ceo@domain.com",
            "policyID": "0123456789ABCDEF",
            "employeeID": "34567",
            "firstName": "Michael",
            "lastName": "Scott",
            "customField1": "ZZZ333",
            "customField2": "BCD345",
            "isTerminated": false
        }
    ]
}

The job uses a JSON list of objects representing employees to provision.

Required employee fields

Field name Type Description
employeeEmail String The email address of the employee
managerEmail String Who the employee should submit reports to
employeeID String Unique, constant external identifier of the employee. It is used to detect email address changes
policyID String The ID of the policy the employee should be invited to

Optional employee fields

Field name Type Description
firstName String First name of the employee in Expensify. Does not overwrite values manually set by the employee in their Expensify account
lastName String Last name of the employee in Expensify. Does not overwrite values manually set by the employee in their Expensify account
customField1 String Custom field, displayed in the "Custom Field 1"
customField2 String Custom field, displayed in the "Custom Field 2"
approvalLimit Integer If passed, determines who the employee should forward the report to when they approve reports over overLimitApprover
overLimitApprover String Who the manager should forward reports to if a report is over approvalLimit. Required if an approvalLimit is specified
limitApprover String Alias for overLimitApprover
isTerminated Boolean If set to true, the employee will be removed from the policyID
domainGroupID String The ID of the domain group this domain member should be added to
approvesTo String If a valid email address is passed, determines who the employee should forward the report to
role String One of user, auditor, admin. If passed, specifies the role of the account in the policy in Expensify. Policy owners and the account running the request cannot have their role demoted from admin.
additionalPolicyIDs JSON array List of additional policies the employee should be added to

Request format

Request payload

  • Via URL
{
    "type": "update",
    "dry-run" : false,
    "credentials": {
        "partnerUserID": "aa_api_domain_com",
        "partnerUserSecret": "xxx",
        "feedUrl": "https://somedomain.com/path/to/employeeData.json",
        "feedUser": "expensify",
        "feedPassword": "xxx"
    },
    "dataSource" : "download",
    "inputSettings": {
        "type": "employees",
        "entity": "generic"
    },
    "onFinish":[
        {"actionName": "email", "recipients":"admin1@domain.com,admin2@domain.com"}
    ]
}
curl -X POST 'https://integrations.expensify.com/Integration-Server/ExpensifyIntegrations' \
    -d 'requestJobDescription={
        "type": "update",
        "dry-run" : false,
        "credentials": {
            "partnerUserID": "aa_api_domain_com",
            "partnerUserSecret": "xxx",
            "feedUrl": "https://somedomain.com/path/to/employeeData.json",
            "feedUser": "expensify",
            "feedPassword": "xxx"
        },
        "dataSource" : "download",
        "inputSettings": {
            "type": "employees",
            "entity": "generic"
        },
        "onFinish":[
            {"actionName": "email", "recipients":"admin1@domain.com,admin2@domain.com"}
        ]
    }'
  • Directly in the request (parameter data)
{
    "type": "update",
    "dry-run" : false,
    "credentials": {
        "partnerUserID": "aa_api_domain_com",
        "partnerUserSecret": "xxx"
    },
    "dataSource" : "request",
    "inputSettings": {
        "type": "employees",
        "entity": "generic"
    },
    "onFinish":[
        {"actionName": "email", "recipients":"admin1@domain.com,admin2@domain.com"}
    ]
}
curl -X POST 'https://integrations.expensify.com/Integration-Server/ExpensifyIntegrations' \
    -d 'requestJobDescription={
        "type": "update",
        "dry-run" : false,
        "credentials": {
            "partnerUserID": "aa_api_domain_com",
            "partnerUserSecret": "xxx"
        },
        "dataSource" : "request",
        "inputSettings": {
            "type": "employees",
            "entity": "generic"
        },
        "onFinish":[
            {"actionName": "email", "recipients":"admin1@domain.com,admin2@domain.com"}
        ]
    }' \
    --data-urlencode 'data@myEmployeeData.json'
  • Via SFTP
{
    "type": "update",
    "dry-run" : false,
    "credentials": {
        "partnerUserID": "aa_api_domain_com",
        "partnerUserSecret": "xxx",
        "sftp": {
            "host": "sftp.domain.com",
            "login": "expensify",
            "password": "[xxx]",
            "port": 22,
            "filename": "/path/to/employeeFile.json"
        }
    },
    "dataSource" : "sftp",
    "inputSettings": {
        "type": "employees",
        "entity": "generic"
    },
    "onFinish":[
        {"actionName": "email", "recipients":"admin1@domain.com,admin2@domain.com"}
    ]
}
curl -X POST 'https://integrations.expensify.com/Integration-Server/ExpensifyIntegrations' \
    -d 'requestJobDescription={
        "type": "update",
        "dry-run" : false,
        "credentials": {
            "partnerUserID": "aa_api_domain_com",
            "partnerUserSecret": "xxx",
            "sftp": {
                "host": "sftp.domain.com",
                "login": "expensify",
                "password": "[xxx]",
                "port": 22,
                "filename": "/path/to/employeeFile.json"
            }
        },
        "dataSource" : "sftp",
        "inputSettings": {
            "type": "employees",
            "entity": "generic"
        },
        "onFinish":[
            {"actionName": "email", "recipients":"admin1@domain.com,admin2@domain.com"}
        ]
    }'

The API call should be made to the regular endpoint https://integrations.expensify.com/Integration-Server/ExpensifyIntegrations. There are three ways to pass the employee data:

requestJobDescription format

Name Format Valid values Description
type String update
credentials JSON object Contains your Expensify API credentials, and information to access the feed by URL if needed.
dataSource String download, request, or sftp Dictates how Expensify should retrieve your employee feed.
inputSettings JSON object See inputSettings below.
Optional elements
dry-run Boolean true, false If set to true, employees will not actually be provisioned or updated. Use this for development.
onFinish JSON array See onFinish You can configure the recipients list of email addresses that should receive a summary email of the changes made by the API.
setEmployeePrimaryPolicy String none (default), new_employees, all_employees none: we will never update an employee's primary policy.
new_employees: we will set an employee's primary policy to the policyID parameter if they were not a member of that policy yet.
all_employees: we will update the primary policy for every employee.
shouldFixApprovalChains Boolean true (default), false Dictates whether Expensify will automatically invite managers (submitsTo), managers’ managers, and so on to policies where they approve reports, if it is not their primary policy.
Name Format Valid values Description
type String employees
entity String Should always be set to generic.

Response format

{
    "responseCode": 200,
    "dry-run": false,
    "updatedEmployeesCount": 3,
    "diff": {
        "diffToAdd": {
            "0123456789ABCDEF": [
                "employee1@domain.com",
                "employee2@domain.com"
            ],
            "ABCDEF0123456789": [
                "employee3@domain.com"
            ]
        },
        "diffToRemove": {
            "B1C7903C636F4A51": [
                "terminatedEmployee@domain.com"
            ]
        }
    },
    "securityGroupEmployeesMap": {
        "407184": [
            "employee1@domain.com",
            "employee2@domain.com"
        ],
        "830936": [
            "employee3@domain.com"
        ]
    },
    "skippedEmployees": [
        {
            "email": "employee6@domain.com",
            "reason": "No policy found for 'Marketing'"
        },
        {
            "email": "employee7@domain.com",
            "reason": "Invalid manager email address 'manager@domain '"
        }
    ]
}

Response codes

Response code Meaning
200 Success
410 Invalid input data, e.g. employee data is missing
500 Other

Response data

Key Meaning
dry-run Whether the job was run in dry-run mode, meaning employee data was not actually updated in Expensify
updatedEmployeesCount Number of employees that were or would have been updated, depending on if the job was run in dry-run mode
diff, diffToAdd, diffToRemove Email addresses of employees that were or would have been invited or removed from policies, grouped by policy IDs
securityGroupEmployeesMap Email addresses of employees that were or would have been assigned to Domain Groups, grouped by domain group IDs
skippedEmployees List of employees that could not be updated, with a reason why