gusto-embedded
Developer-friendly & type-safe Typescript SDK specifically catered to leverage gusto-embedded API.
[!IMPORTANT]
This SDK is not yet ready for production use. To complete setup please follow the steps outlined in your workspace. Delete this section before > publishing to a package manager.
Summary
Gusto API: Welcome to Gusto's Embedded Payroll API documentation!
Table of Contents
SDK Installation
The SDK can be installed with either npm, pnpm, bun or yarn package managers.
NPM
npm add @gusto/embedded-api
npm add @tanstack/react-query react react-dom
PNPM
pnpm add @gusto/embedded-api
pnpm add @tanstack/react-query react react-dom
Bun
bun add @gusto/embedded-api
bun add @tanstack/react-query react react-dom
Yarn
yarn add @gusto/embedded-api zod
yarn add @tanstack/react-query react react-dom
[!NOTE]
This package is published as an ES Module (ESM) only. For applications using
CommonJS, use await import("@gusto/embedded-api")
to import and use this package.
Requirements
For supported JavaScript runtimes, please consult RUNTIMES.md.
SDK Example Usage
Example
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded({
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.introspection.getInfo({});
console.log(result);
}
run();
Authentication
Per-Client Security Schemes
This SDK supports the following security scheme globally:
companyAccessAuth | http | HTTP Bearer | GUSTOEMBEDDED_COMPANY_ACCESS_AUTH |
To authenticate with the API the companyAccessAuth
parameter must be set when initializing the SDK client instance. For example:
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded({
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.introspection.getInfo({});
console.log(result);
}
run();
Per-Operation Security Schemes
Some operations in this SDK require the security scheme to be specified at the request level. For example:
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded();
async function run() {
const result = await gustoEmbedded.companies.createPartnerManaged({
systemAccessAuth: process.env["GUSTOEMBEDDED_SYSTEM_ACCESS_AUTH"] ?? "",
}, {
requestBody: {
user: {
firstName: "Frank",
lastName: "Ocean",
email: "frank@example.com",
phone: "2345558899",
},
company: {
name: "Frank's Ocean, LLC",
tradeName: "Frank’s Ocean",
ein: "123456789",
contractorOnly: false,
},
},
});
console.log(result);
}
run();
Available Resources and Operations
Available methods
- getAll - Get all ACH transactions for a company
- get - Get suspensions for this company
- suspend - Suspend a company's account
- getDownloadUrl - Get a temporary url to download the Company Attachment file
- getDetails - Get Company Attachment Details
- getList - Get List of Company Attachments
- create - Create Company Attachment and Upload File
- getAll - Get all company forms
- get - Get a company form
- getPdf - Get a company form pdf
- sign - Sign a company form
- getAll - Get all contractor documents
- get - Get a contractor document
- getPdf - Get the contractor document pdf
- sign - Sign a contractor document
- list - Get all contractor forms
- get - Get a contractor form
- getPdf - Get the contractor form pdf
- generate1099 - Generate a 1099 form [DEMO]
- getBankAccounts - Get all contractor bank accounts
- get - Get a contractor's payment method
- update - Update a contractor's payment method
- getReceipt - Get a single contractor payment receipt
- fund - Fund a contractor payment [DEMO]
- create - Create a contractor payment
- list - Get contractor payments for a company
- get - Get a single contractor payment
- delete - Cancel a contractor payment
- create - Create a custom earning type
- list - Get all earning types for a company
- update - Update an earning type
- delete - Deactivate an earning type
- generateW2 - Generate a W2 form [DEMO]
- list - Get all employee forms
- get - Get an employee form
- getPdf - Get the employee form pdf
- sign - Sign an employee form
- get - Get Federal Tax Details
- update - Update Federal Tax Details
- get - Get a generated document
- update - Update a historical employee
- get - Get a company's holiday pay policy
- create - Create a holiday pay policy for a company
- update - Update a company's holiday pay policy
- delete - Delete a company's holiday pay policy
- addEmployees - Add employees to a company's holiday pay policy
- removeEmployees - Remove employees from a company's holiday pay policy
- get - Get a company industry selection
- update - Update a company industry selection
- get - Retrieve invoicing data for companies
- get - Get a company's payment configs
- update - Update a company's payment configs
- get - Get all recovery cases for a company
- redebit - Initiate a redebit for a recovery case
- create - Create a signatory
- list - Get all company signatories
- invite - Invite a signatory
- update - Update a signatory
- delete - Delete a signatory
- get - Get State Tax Requirements
- updateState - Update State Tax Requirements
- getAll - Get All Tax Requirement States
- get - Get a single Wire In Request
- submit - Submit a wire in request
- list - Get all Wire In Requests for a company
Standalone functions
All the methods listed above are available as standalone functions. These
functions are ideal for use in applications running in the browser, serverless
runtimes or other environments where application bundle size is a primary
concern. When using a bundler to build your application, all unused
functionality will be either excluded from the final bundle or tree-shaken away.
To read more about standalone functions, check FUNCTIONS.md.
Available standalone functions
React hooks with TanStack Query
React hooks built on TanStack Query are included in this SDK.
These hooks and the utility functions provided alongside them can be used to
build rich applications that pull data from the API using one of the most
popular asynchronous state management library.
To learn about this feature and how to get started, check
REACT_QUERY.md.
[!WARNING]
This feature is currently in preview and is subject to breaking changes
within the current major version of the SDK as we gather user feedback on it.
Available React hooks
File uploads
Certain SDK methods accept files as part of a multi-part request. It is possible and typically recommended to upload files as a stream rather than reading the entire contents into memory. This avoids excessive memory consumption and potentially crashing with out-of-memory errors when working with very large files. The following example demonstrates how to attach a file stream to a request.
[!TIP]
Depending on your JavaScript runtime, there are convenient utilities that return a handle to a file without reading the entire contents into memory:
- Node.js v20+: Since v20, Node.js comes with a native
openAsBlob
function in node:fs
.
- Bun: The native
Bun.file
function produces a file handle that can be used for streaming file uploads.
- Browsers: All supported browsers return an instance to a
File
when reading the value from an <input type="file">
element.
- Node.js v18: A file stream can be created using the
fileFrom
helper from fetch-blob/from.js
.
import { GustoEmbedded } from "@gusto/embedded-api";
import { openAsBlob } from "node:fs";
const gustoEmbedded = new GustoEmbedded({
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.companyAttachments.create({
companyId: "<id>",
requestBody: {
document: await openAsBlob("example.file"),
category: "gep_notice",
},
});
console.log(result);
}
run();
Retries
Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK.
To change the default retry strategy for a single API call, simply provide a retryConfig object to the call:
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded({
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.introspection.getInfo({}, {
retries: {
strategy: "backoff",
backoff: {
initialInterval: 1,
maxInterval: 50,
exponent: 1.1,
maxElapsedTime: 100,
},
retryConnectionErrors: false,
},
});
console.log(result);
}
run();
If you'd like to override the default retry strategy for all operations that support retries, you can provide a retryConfig at SDK initialization:
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded({
retryConfig: {
strategy: "backoff",
backoff: {
initialInterval: 1,
maxInterval: 50,
exponent: 1.1,
maxElapsedTime: 100,
},
retryConnectionErrors: false,
},
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.introspection.getInfo({});
console.log(result);
}
run();
Error Handling
GustoEmbeddedError
is the base class for all HTTP error responses. It has the following properties:
error.message | string | Error message |
error.httpMeta.response | Response | HTTP response. Access to headers and more. |
error.httpMeta.request | Request | HTTP request. Access to headers and more. |
error.data$ | | Optional. Some errors may contain structured data. See Error Classes. |
Example
import { GustoEmbedded } from "@gusto/embedded-api";
import { GustoEmbeddedError } from "@gusto/embedded-api/models/errors/gustoembeddederror.js.js";
import { UnprocessableEntityErrorObject } from "@gusto/embedded-api/models/errors/unprocessableentityerrorobject.js";
const gustoEmbedded = new GustoEmbedded();
async function run() {
try {
const result = await gustoEmbedded.companies.createPartnerManaged({
systemAccessAuth: process.env["GUSTOEMBEDDED_SYSTEM_ACCESS_AUTH"] ?? "",
}, {
requestBody: {
user: {
firstName: "Frank",
lastName: "Ocean",
email: "frank@example.com",
phone: "2345558899",
},
company: {
name: "Frank's Ocean, LLC",
tradeName: "Frank’s Ocean",
ein: "123456789",
contractorOnly: false,
},
},
});
console.log(result);
} catch (error) {
if (error instanceof GustoEmbeddedError) {
console.log(error.message);
console.log(error.httpMeta.response.status);
console.log(error.httpMeta.response.headers);
console.log(error.httpMeta.request);
if (error instanceof UnprocessableEntityErrorObject) {
console.log(error.data$.errors);
}
}
}
}
run();
Error Classes
Primary error:
Less common errors (12)
Network errors:
Inherit from GustoEmbeddedError
:
UnprocessableEntityErrorObject
: Unprocessable Entity This may happen when the body of your request contains errors such as invalid_attribute_value
, or the request fails due to an invalid_operation
. See the Errors Categories guide for more details. Applicable to 139 of 260 methods.*
NotFoundErrorObject
: Not Found The requested resource does not exist. Make sure the provided ID/UUID is valid. Status code 404
. Applicable to 4 of 260 methods.*
UnprocessableEntityErrorObject1
: Unprocessable Entity This may happen when the body of your request contains errors such as invalid_attribute_value
, or the request fails due to an invalid_operation
. See the Errors Categories guide for more details. Status code 422
. Applicable to 4 of 260 methods.*
PayrollBlockersError
: Payroll Blockers Error For detailed information, see the Payroll Blockers guide. Status code 422
. Applicable to 4 of 260 methods.*
DeleteV1CompanyBenefitsCompanyBenefitIdResponseBody
: Unprocessable Entity. Status code 422
. Applicable to 1 of 260 methods.*
CompanySuspensionCreationErrors
: Unprocessable Entity This may happen when the body of your request contains errors such as invalid_attribute_value
, or the request fails due to an invalid_operation
. See the Errors Categories guide for more details. Status code 422
. Applicable to 1 of 260 methods.*
ResponseValidationError
: Type mismatch between the data returned from the server and the structure expected by the SDK. See error.rawValue
for the raw value and error.pretty()
for a nicely formatted multi-line string.
* Check the method documentation to see if the error is applicable.
Server Selection
Select Server by Name
You can override the default server globally by passing a server name to the server: keyof typeof ServerList
optional parameter when initializing the SDK client instance. The selected server will then be used as the default on the operations that use it. This table lists the names associated with the available servers:
demo | https://api.gusto-demo.com | Demo |
prod | https://api.gusto.com | Prod |
Example
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded({
server: "prod",
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.introspection.getInfo({});
console.log(result);
}
run();
Override Server URL Per-Client
The default server can also be overridden globally by passing a URL to the serverURL: string
optional parameter when initializing the SDK client instance. For example:
import { GustoEmbedded } from "@gusto/embedded-api";
const gustoEmbedded = new GustoEmbedded({
serverURL: "https://api.gusto-demo.com",
companyAccessAuth: process.env["GUSTOEMBEDDED_COMPANY_ACCESS_AUTH"] ?? "",
});
async function run() {
const result = await gustoEmbedded.introspection.getInfo({});
console.log(result);
}
run();
Custom HTTP Client
The TypeScript SDK makes API calls using an HTTPClient
that wraps the native
Fetch API. This
client is a thin wrapper around fetch
and provides the ability to attach hooks
around the request lifecycle that can be used to modify the request or handle
errors and response.
The HTTPClient
constructor takes an optional fetcher
argument that can be
used to integrate a third-party HTTP client or when writing tests to mock out
the HTTP client and feed in fixtures.
The following example shows how to use the "beforeRequest"
hook to to add a
custom header and a timeout to requests and how to use the "requestError"
hook
to log errors:
import { GustoEmbedded } from "@gusto/embedded-api";
import { HTTPClient } from "@gusto/embedded-api/lib/http";
const httpClient = new HTTPClient({
fetcher: (request) => {
return fetch(request);
}
});
httpClient.addHook("beforeRequest", (request) => {
const nextRequest = new Request(request, {
signal: request.signal || AbortSignal.timeout(5000)
});
nextRequest.headers.set("x-custom-header", "custom value");
return nextRequest;
});
httpClient.addHook("requestError", (error, request) => {
console.group("Request Error");
console.log("Reason:", `${error}`);
console.log("Endpoint:", `${request.method} ${request.url}`);
console.groupEnd();
});
const sdk = new GustoEmbedded({ httpClient });
Debugging
You can setup your SDK to emit debug logs for SDK requests and responses.
You can pass a logger that matches console
's interface as an SDK option.
[!WARNING]
Beware that debug logging will reveal secrets, like API tokens in headers, in log messages printed to a console or files. It's recommended to use this feature only during local development and not in production.
import { GustoEmbedded } from "@gusto/embedded-api";
const sdk = new GustoEmbedded({ debugLogger: console });
You can also enable a default debug logger by setting an environment variable GUSTOEMBEDDED_DEBUG
to true.
Development
Maturity
This SDK is in beta, and there may be breaking changes between versions without a major version update. Therefore, we recommend pinning usage
to a specific package version. This way, you can install the same version each time without breaking changes unless you are intentionally
looking for the latest version.
Contributions
While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation.
We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.