Web Services API

Please note that the Expensify API is not accepting new applications at this time. Enterprise customers looking to integrate with Expensify should visit the Integration Server documentation.

The Expensify Web Services API lets you query and manipulate Expensify from your own application. For example, you can use it to pull transaction data for a specific card or submit a picture of a receipt. All the data returned is in JSON format, so it is easy to use with a variety of languages.

Live Demo

To see it in action, just click this really long link (change the email address to your own before clicking to be more secure):

https://example.expensify.com/partnerSignin?partnerPassword=insecure&email=test@expensify.com&partnerUserID=test@expensify.com&partnerUserSecret=insecure&enterAt=newbankform&exitTo=https%3A%2F%2Fexample.expensify.com%2Fapi%3Fcommand%3DGetCard%26contentType%3Dtext%2Fplain

This uses the newbankform entry point of the whitelabel API to import a new credit card, and then redirects to the "GetCard" API command below to download the corresponding JSON card object.

Note: This is just for demonstration purposes -- you would never put live passwords into a public URL. But it's a handy way to show off the API.

Executing API Commands

Expensify API commands are standard HTTPS calls. To call the API:

  1. Build a URL corresponding to the command you want to execute. All API commands share a common URL syntax:

    https://www.expensify.com/api?partnerName=<partnerName>&command=<command>&...
    

    Or:

    https://<partnerName>.expensify.com/api?command=<command>&...
    

    Replace with the name of the command to execute, and then add whichever parameters that command requires to function. For example, all API commands (with the exception of Authenticate and CreateAccount) require a valid authToken, as well as any additional parameters listed below.

  2. Download that URL using a standard HTTP GET request. Most languages have high-level functions that will do this for you (you might consider fopen() if you're using PHP or XMLHttpRequest if you're using Javascript).

  3. Decode the JSON response. Most languages have a function to do this (take a look at json_decode() in PHP).

  4. Check the value of jsonCode in the JSON response. jsonCode is set to the response value of the actual command executed. This is different than the HTTP response code provided by the webserver, which merely indicates whether a valid request was received and a valid JSON response was returned For example: the HTTP request to the webserver can return "200 OK" to signify that a JSON response was returned, but that response can include an jsonCode that is something else.

jsonCode Values

In addition to the specific jsonCode values detailed in each command description, all commands can return any of the following codes:

  • 200 OK
  • 400 Unrecognized command.
  • 402 Missing argument.
  • 407 Malformed or expired token.
  • 408 Account deleted.
  • 411 Insufficient privileges
  • 500 Aborted.
  • 501 DB transaction error.
  • 502 Query error.
  • 503 Query response error.
  • 504 Unrecognized object state.

Miscellaneous Notes

A couple important notes that don't quite fit anywhere else:

  • All currency amounts are given as integers, depending on the currency. For example, USD is reported as a number of whole-cents (dollars * 100).

  • The user described by the authToken must have the appropriate authorization to perform the action, otherwise an error will be returned.

  • To ease development and debugging, as well as to simplify implementation over direct SSL connections, whenever required you can provide the partnerPassword and partnerUser directly in the request query string. However, for additional security or when the query string might be logged or presented in an insecure fashion, you are encouraged to provide the partnerPassword and/or partnerUserSecret in an encrytped SSO object.

Core Objects

Expensify comprises several core data objects:

Accounts

Each real-world user has one "account". Accounts have:

  • One or more logins
  • One or more cards
  • Zero or more reports, receipts, etc.
account Object:
{
    email       : The accounts primary email address.
}

Logins

Each account has one or more "logins". A login includes three components:

  • partnerName: The name of the partner that created this login (eg "expensify.com" or "salesforce.com"). We assign this to you.

  • partnerUserID: The userID by which the you know this user (eg, "dbarrett@expensify.com" or "dbarrett"). The partnerUserID is often an email address (for those partners that use emails as userIDs), but needn't always be.

  • partnerUserSecret: The secret associated with this user by the partner (eg, a partner-selected password or other unique identifier). Do not set the partnerUserSecret to the password the user uses to sign in to your service: if you do, and if the user changes their password, you will then be unable to access their linked Expensify account.

Note: For more information, please see Expensify Single-Signon.

login Object:
{
    partnerName: name of the partner site
    partnerUserID: userID of this user on the partner site
}

Cards

Each account has one or more "cards". Cards are essentially buckets of "transactions". Cards come in a few varieties:

  • Cash: Every account is pre-populaed with a "cash" card, which records cash expenses.
  • Prepaid: Represents an Expensify Prepaid MasterCard.
  • Imported: Represents an existing credit card imported from a banking website.
Card Object:
{
    created         : "yyyy-mm-dd hh:mm:ss" - in UTC
    closed          : "yyyy-mm-dd hh:mm:ss" - in UTC
    cardID          :
    fundID          : Prepaid only.
    cardName        : Name assigned to card, or masked PAN in the case of most imported cards.
    state           : Card state.
    bank            : Imported only.  For uploaded ofx files set to 'upload'.
    budget          : Prepaid only.
    cardBudget      : Prepaid only.  Same as budget.  Use budget.
    balance         : Prepaid only.  Current card balance.
    cardNumber      : Masked PAN.
    addressName     : The name of the card holder.  Not all cards have this.
    addressStreet   :
    addressCity     :
    addressState    : Two letters.
    addressZip      : xxxxx
    lastImport      : "yyyy-mm-dd hh:mm:ss" - in UTC.  The last time this card was imported.
    lastImportResult:
    reimbursable    : Are the transactions on this card reimbursable.
}

Reports

Every report is in one of eight states:

  • OPEN: The report is in the process of being created. New transactions can be added in this state.

  • SUBMITTED: It has been submitted to a manager for approval. It is frozen in this state and cannot be modified. From here it can be APPROVED or rejected (sent back to OPEN).

  • MANUALREIMBURSED: The manager has repaid this report without using expensify. The report is now finalized and cannot be modified. This should be renamed.

  • BILLING: The manager's card or bank account is being billed. The report is put into this state after an AUTH or ACH xfer is queued.

  • READYTOPAY: We succeeded in charging the manager and we are ready to pay the employee. The report is automatically put into this state after a successful transfer.

  • PAYING: The employees card or bank account is being credited. The report is put into this state after an AUTH or ACH xfer is queued.

  • AUTOREIMBURSED: Done. This is where we want to be. The report is automatically put into this state after a successful transfer.

Report Object:
{
    created         : "yyyy-mm-dd hh:mm:ss" - in UTC
    submitted       : "yyyy-mm-dd hh:mm:ss" - in UTC
    approved        : "yyyy-mm-dd hh:mm:ss" - in UTC
    reportID        :
    accountID       :
    managerID       : The accountID of the next manager to act on this report.
    reportName      :
    state           : Report state.
    cachedTotal     : Cached report total.  Only available after a report was submitted.
    currency        : Currency of total.
    accountEmail    : The email address of the submitter.
    managerEmail    : The email address of the next manager to act on this report..
}
sharedReport Object:
{
    email           :
    permissions     : read, write, share, own
}
reportNameValuePairs Object:
{
    <name0> : value0
    <name1> : value1
    .
    .
    <nameN> : valueN
}
reportAction Object:
{
    created         : "yyyy-mm-dd hh:mm:ss" - in UTC
    action          : ReportAction
    message         : Optional message sent with action.
    accountEmail    : The email address associated with 'accountID'
}

Transactions

Transaction Object:
{
    created         : "yyyy-mm-dd hh:mm:ss" - in UTC
    transactionID   :
    cardID          :
    reportID        : -1 if deleted.
    receiptID       :
    receiptFilename :
    amount          : In pennies.  Negative if sale.  Positive if return.
    merchant        :
    mcc             : Not always provided.
    state           : Transaction state.
    comment         :
    tag             :
    externalID      : FITID or auth code.  This is used in eReceipts.
    memo            : This is used in eReceipts.
    inserted        : "yyyy-mm-dd hh:mm:ss" - in UTC  When this transaction was added to the db.
    currency        : A blank currency is the same as USD.
    modifiedCreated : "yyyy-mm-dd hh:mm:ss" - in UTC  For imported cards, created is the posted date.  We need to keep the posted date for eReceipts.  This was added to allow the user to set the sales date of the transaction.
    modifiedAmount  : We need to keep the original amount for eReceipts.  This was added in case a user wants to change the amount of a transaction.
    modifiedMerchant: We need to keep the original merchant for the eReceipt.  This was added in case a user wants to pretty up the merchant line.
    modifiedMCC     : Our best guess at the MCC.
    category        :
    bank            :
    cardNumber      : Masked PAN.
    reimbursable    : Is this expense reimbursable.
    billable        : Is this expense billable.
}

Receipts

receipt Object:
{
    receiptID       : Unique ID of this receipt.
    transactionID   : Transaction it's attached to.
    filename        : Name of the image on s3.
    created         : Date the receipt was uploaded.
    state           : OPEN or DELETED
}

Name Value Pairs

nameValuePairs Object:
{
    <name0> : value0
    <name1> : value1
    .
    .
    <nameN> : valueN
}
sharedInNameValuePair Object:
{
    email       :
    name        :
    value       :
    permissions :
}
sharedOutNameValuePair Object:
{
    ownerEmail  :
    email       :
    name        :
    permissions :
}

Read-Only Command Definitions

Command Definitions for commands that don't require authentication:

Get(authToken, returnValueList, ...)

Gets all the values specified in the returnValueList with the optional params used to select specific cards, reports, transactions belonging to a specific card or report, etc. Below is a list of values that can be passed in returnValueList and the optional parameters they can take:

  • account(),
  • transactionList([cardID|reportIDList|transactionID], [startDate], [endDate], [limit], [offset])
  • cardList([cardID])
  • reportList([reportIDList], [state], [startDate], [endDate], [limit], [offset])
  • sharedReportList(reportID)
  • reportActionList(reportID)
  • reportNameValuePairs(reportID)
  • commercialCardFeedList(domain)
  • receiptList([receiptID], [startDate], [endDate], [limit], [offset], [detailsOnly])
  • nameValuePairs([name])
  • sharedInNameValuePairList([name])
  • sharedOutNameValuePairList([name])
  • loginList()
  • purchaseList()
  • policyIDList()
  • policySummaryList()
  • policyList([policyIDList])

Specific response codes (in addition to "Common Codes" above):

  • (none)

JSON response for each returnValueList:

{
    account                        : account object
    transactionList                :[ transaction objects ]
    cardList                       :[ card objects ]
    reportList                     :[ report objects ]
    starredReportList              :[ report objects ]
    sharedReportList               :[]
    reportActionList               :[ reportAction objects ]
    reportNameValuePairs           : reportNameValuePairs object
    receiptList                    :[ receipt objects ]
    nameValuePairs                 : nameValuePairs object
    sharedInNameValuePairList      :[ sharedInNameValuePair objects ]
    sharedOutNameValuePairList     :[ sharedOutNameValuePair objects ]
    loginList                      :[ login objects ]
    purchaseList                   :[ purchase objects ]
    policyIDList                   :[ policyID strings ]
    policySummaryList              :[ policyID objects ]
    policyList                     :[ policy objects ]
}

Read-Write Command Definitions

Command Definitions for commands that require external communication:

Authenticate(partnerName, partnerPassword, partnerUserID, partnerUserSecret, [useExpensifyLogin]) | (partnerName, sso))

GET https://www.expensify.com/api?command=Authenticate&partnerName=XXXX&partnerPassword=XXXX&partnerUserID=XXXX&partnerUserSecret=XXXX&useExpensifyLogin=false

GET https://www.expensify.com/api?command=Authenticate&partnerName=XXXX&partnerUserID=XXXX&sso=XXXX

First authenticates the caller using the (partnerName, partnerPassword) pair -- both of which are assigned to you. Next, authenticates the user using the (partnerUserID, partnerUserSecret) pair specified in the corresponding call to CreateLogin. On success, returns an "authToken", which grants access to this account in future calls. The authToken returned will expire one hour from creation, but most other API commands will "refresh" the authToken by returning a new one with an additional hour before expiration.

Specific response codes (in addition to "Common Codes" above):

  • 401 Password is wrong.
  • 404 Account not found.
  • 405 Email not validated.

JSON response:

    {
        email       : Primary email address.
        accountID   : Unique identifier of this account.
        authToken   : Encrypted accountID and expiration time.
    }

CreateAccount(partnerName, partnerPassword, email, referer, [password], [inviterID], [lastIP], [template], [message])

GET https://www.expensify.com/api?command=CreateAccount&email=XXXX&partnerName=XXXX&partnerPassword=XXXX&referer=XXXX

GET https://www.expensify.com/api?command=CreateAccount&email=XXXX&partnerName=XXXX&sso=XXXX&referer=XXXX

Creates a new account with this name and password. Returns the same as Authenticate, so a user gets a free pass when they first get to the site. However, when they first try to Authenticate they'll be told they need to validate their email address by calling ValidateEmail. Once done, then they can Authenticate just fine. Referer is used to track the account creation source; e.g. "iphone" for the iPhone app.

Specific response codes (in addition to "Common Codes" above):

  • 300 Already exists

JSON response:

{
    email           :
    accountID       :
    authToken       :
}

CreateTransaction(authToken, created, amount, [currency], merchant, [comment], [category], [tag], [receiptID], [reimbursable=true], [billable=false])

GET https://www.expensify.com/api?command=CreateTransaction&authToken=XXXX&created=YYYY&amount=ZZZZ&currency=CCC&merchant=AAAA

Adds a cash transaction to the cash card for this account.

Specific response codes (in addition to "Common Codes" above):

  • 404 Cash card not found (should never happen)

JSON response:

{
    *           : Transaction object.
}

CreateReceipt(email, authToken, [transactionID])

POST https://www.expensify.com/api?command=CreateReceipt&email=XXXX&authToken=XXXX&transactionID=XXXX

Creates a new receipt with the POSTed image. The POSTed image should have a content-type that is supported by Expensify, i.e. image/jpeg, image/jpg, image/png, image/gif. If a transactionID is given, the image is associated with that transaction, otherwise, it remains unattached. The call returns a jsonCode of either 200 or 404.

Specific response codes (in addition to "Common Codes" above):

  • 404 Error. Returned if there's an error with the uploaded image.

JSON response:

{
    jsonCode    :
}

SetNameValuePair(authToken, name, [value])

GET https://www.expensify.com/api?command=SetNameValuePair&authToken=XXXX&name=YYYY&value=ZZZZ

Sets the value associated with a name. If that name doesn't already exist, it will be added. If a blank value is passed, that name will be deleted.

Specific response codes (in addition to "Common Codes" above):

  • (none)

JSON response:

{}

SetSharedNameValuePair(authToken, ownerEmail, name, value)

GET https://www.expensify.com/api?command=SetSharedNameValuePair&authToken=XXXX&ownerEmail=AAAA&name=YYYY&value=ZZZZ

Sets the value associated with an email and name. You need write access to do this.

Specific response codes (in addition to "Common Codes" above):

  • 401 Permission denied.
  • 404 NameValuePair not found.

JSON response:

{}

CreateReport(authToken, reportName)

GET https://www.expensify.com/api?command=CreateReport&authToken=XXXX&reportName=YYYY

Creates an empty report.

Specific response codes (in addition to "Common Codes" above):

  • (none)

JSON response:

{
    *               : report object
}

SetReportNameValuePair(authToken, reportID, name, value)

GET https://www.expensify.com/api?command=SetReportNameValuePair&authToken=XXXX&reportID=AAAA&name=YYYY&value=ZZZZ

Sets the value associated with a name for the specified report. If that name doesn't already exist, it will be added. If a blank value is passed, that name will be deleted.

Specific response codes (in addition to "Common Codes" above):

  • 404 Report not found

JSON response:

{}

RenameReport(authToken, reportID, reportName)

GET https://www.expensify.com/api?command=RenameReport&authToken=XXXX&reportID=YYYY&reportName=ZZZZ

Renames a report. Can be done by current owner.

Specific response codes (in addition to "Common Codes" above):

  • 404 Report not found

JSON response:

{}

DeleteReport(authToken, reportID)

GET https://www.expensify.com/api?command=DeleteReport&authToken=XXXX&reportID=YYYY

Deletes an OPEN report.

Specific response codes (in addition to "Common Codes" above):

  • 404 Report not found.

JSON response:

{}

ReportTransactions(authToken, reportID, transactionIDList)

GET https://www.expensify.com/api?command=ReportTransactions&authToken=XXXX&reportID=YYYY&transactionIDList=ZZZZ

Adds a comma-delimited list of transaction IDs to a report.

Specific response codes (in addition to "Common Codes" above):

  • 404 Transaction or report not found.

JSON response:

{}

AttachReceipt(authToken, receiptID, transactionID)

GET https://www.expensify.com/api?command=AttachReceipt&authToken=XXXX&receiptID=YYYY&transactionID=ZZZZ

Attaches a receipt to a given transaction. Set receiptID=0 to detach a receipt from a transaction.

Specific response codes (in addition to "Common Codes" above):

  • 404 Transaction or receipt not found.

JSON response:

{}

DeleteReceipt(authToken, receiptID)

GET https://www.expensify.com/api?command=DeleteReceipt&authToken=XXXX&receiptID=YYYY

Put a receipt to the DELETED state.

Specific response codes (in addition to "Common Codes" above):

  • 404 Receipt not found.

JSON response:

{}