@panoptic-it-solutions/autotask-client v0.7.4
@panoptic-it-solutions/autotask-client
A Node.js SDK for interacting with the Autotask REST API, featuring built-in rate limiting, TypeScript support, automatic pagination, and relationship embedding.
Table of Contents
- Installation
- Configuration
- Usage
- Filtering
- Pagination
- Fetching Related Entities (
includeClause) - Key Types and Interfaces
- Error Handling
- Rate Limiting Details
- Source Code Structure Overview
- Platform-Specific Configuration Examples
- Contributing
- License
Installation
npm install @panoptic-it-solutions/autotask-client
# or
yarn add @panoptic-it-solutions/autotask-client
# or
pnpm add @panoptic-it-solutions/autotask-clientConfiguration
This SDK requires the following environment variables to be set for basic Autotask API interaction:
AUTOTASK_USERNAME: Your Autotask API User's username (Access Key in Autotask).AUTOTASK_SECRET: Your Autotask API User's secret (Secret Key in Autotask).AUTOTASK_API_INTEGRATION_CODE: Your Autotask API Tracking Identifier.AUTOTASK_API_URL: The base URL for the Autotask REST API specific to your zone (e.g.,https://webservices[XX].autotask.net/ATServicesRest/v1.0/). Ensure this ends with a trailing slash.
Upstash Redis for Rate Limiting (Recommended)
For robust, distributed rate limiting (essential for serverless environments or horizontally scaled applications), the client integrates with Upstash Redis.
Required Environment Variables for Upstash Rate Limiting:
UPSTASH_REDIS_REST_URL: The REST URL for your Upstash Redis instance.UPSTASH_REDIS_REST_TOKEN: The read/write token for your Upstash Redis instance.
You can find these credentials in your Upstash console after creating a Redis database.
Optional Upstash Rate Limit Configuration (via AutotaskClientConfig):
Default rate limits (e.g., 5 requests per second, 5000 per hour) can be overridden in the AutotaskClientConfig (see Client Instantiation). Environment variables UPSTASH_HOURLY_LIMIT and UPSTASH_SECONDLY_LIMIT are no longer directly used for overriding these; use the config object instead.
If Upstash credentials are not provided in the config, the client will operate without proactive distributed rate limiting, relying solely on reactive backoff for Autotask API 429 responses. This is not recommended for production or distributed environments.
Example .env File
It's recommended to use a library like dotenv to manage these variables. Create a .env file in the root of your project:
# Autotask API Credentials
AUTOTASK_USERNAME="YOUR_API_USERNAME"
AUTOTASK_SECRET="YOUR_API_SECRET"
AUTOTASK_API_INTEGRATION_CODE="YOUR_API_INTEGRATION_CODE"
AUTOTASK_API_URL="https://your-zone.autotask.net/ATServicesRest/v1.0/"
# Upstash Redis Credentials (Recommended for Rate Limiting)
UPSTASH_REDIS_REST_URL="your_upstash_redis_url_here"
UPSTASH_REDIS_REST_TOKEN="your_upstash_redis_token_here"Load these variables at the beginning of your application:
import * as dotenv from "dotenv";
dotenv.config();Usage
Client Instantiation
import {
AutotaskClient,
AutotaskClientConfig,
LogLevel, // For setting log levels
} from "@panoptic-it-solutions/autotask-client";
import * as dotenv from "dotenv";
// Load environment variables
dotenv.config();
const clientConfig: AutotaskClientConfig = {
userName: process.env.AUTOTASK_USERNAME!,
secret: process.env.AUTOTASK_SECRET!,
integrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
apiBaseUrl: process.env.AUTOTASK_API_URL!, // Renamed from baseUrl
// Optional Upstash configuration for rate limiting
upstash: {
upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL, // Can be undefined if not using Upstash
upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN, // Can be undefined
// hourlyLimit: 4500, // Example: Override default hourly limit
// secondlyLimit: 4, // Example: Override default per-second limit
},
// Optional retry configuration (client has sensible defaults)
retryConfig: {
maxRetries: 5,
initialBackoffMs: 1000,
},
// Optional logging configuration
logLevel: LogLevel.INFO, // Supported levels: DEBUG, INFO, WARN, ERROR, NONE
// logger: new MyCustomLogger(), // Optionally provide a custom logger instance
};
const client = new AutotaskClient(clientConfig);Querying a Single Page of Entities (queryEntities)
Use queryEntities to fetch a single page of entities.
import {
AutotaskQueryOptions,
AllAutotaskEntityNames,
AutotaskEntityTypeMap,
SdkClientPagedResponse,
} from "@panoptic-it-solutions/autotask-client";
// Assume 'client' is an instantiated AutotaskClient
async function getFirstPageOfActiveCompanies() {
try {
const entityName: AllAutotaskEntityNames = "Companies";
const options: AutotaskQueryOptions = {
fields: ["id", "companyName", "companyType", "isActive"],
filter: [
// Refer to Autotask API docs for correct field name (e.g., 'status' or 'companyStatus') and value
{ field: "isActive", op: "eq", value: true },
],
sdkPageSize: 10,
includeTotalCount: true, // Get total count for pagination info on the first request
};
const response: SdkClientPagedResponse<AutotaskEntityTypeMap["Companies"]> =
await client.queryEntities(entityName, options);
console.log(
`Fetched page ${response.currentPage} of ${
response.totalPages ?? "unknown"
}, containing ${response.items.length} companies.`
);
if (response.totalItemsAvailable !== undefined) {
console.log(
`Total active companies matching filter: ${response.totalItemsAvailable}`
);
}
response.items.forEach((company) => {
console.log(
`- ID: ${company.id}, Name: ${company.companyName}, Type: ${company.companyType}, Active: ${company.isActive}`
);
});
if (response.hasNextPage && response.continuationToken) {
console.log(
"\nTo fetch the next page, use this continuationToken:",
response.continuationToken
);
// Example: Fetching next page
// const nextPageOptions: AutotaskQueryOptions = {
// continuationToken: response.continuationToken,
// sdkPageSize: 10 // sdkPageSize can be repeated or omitted (uses original if omitted)
// };
// const nextPageResponse = await client.queryEntities(entityName, nextPageOptions);
}
} catch (error) {
console.error("Error fetching companies:", error);
// Handle specific errors, e.g., AutotaskApiError, RateLimitError
}
}
getFirstPageOfActiveCompanies();Fetching All Pages Automatically (queryAllEntities)
Use queryAllEntities to retrieve all items matching a query, with the SDK handling pagination transparently.
import {
AutotaskQueryOptions,
AllAutotaskEntityNames,
} from "@panoptic-it-solutions/autotask-client";
// Assume 'client' is an instantiated AutotaskClient
async function fetchAllActiveContacts() {
try {
const entityName: AllAutotaskEntityNames = "Contacts";
const options: AutotaskQueryOptions = {
filter: [{ field: "isActive", op: "eq", value: true }],
fields: ["id", "firstName", "lastName", "emailAddress"],
sdkPageSize: 50, // SDK fetches in chunks of 50 from the API
};
console.log("Fetching all active contacts...");
const allContacts = await client.queryAllEntities(
entityName,
options,
// Optional progress callback
(progress) => {
const total =
progress.totalKnownItems !== undefined
? ` of ${progress.totalKnownItems}`
: "";
console.log(
`Progress: Page ${progress.currentPageNumber} fetched. ` +
`${progress.totalItemsFetchedSoFar}${total} contacts retrieved.`
);
},
// Optional: fetch total count upfront for more accurate progress reporting
{ fetchTotalCountForProgress: true }
);
console.log(`Successfully fetched ${allContacts.length} active contacts.`);
// Process allContacts array
} catch (error) {
console.error("Failed to fetch all contacts:", error);
}
}
fetchAllActiveContacts();Filtering
The SDK supports powerful filtering capabilities when querying entities.
Filter Structure (AutotaskApiFilter)
Filters are provided as an array of AutotaskApiFilter objects to the filter property in AutotaskQueryOptions. Each filter object has the following structure:
interface AutotaskApiFilter {
field: string;
op: AutotaskApiFilterOp; // Union of supported operator strings
value: string | number | boolean | (string | number)[]; // Value depends on 'op'
udf?: boolean; // Set to true if 'field' is a User-Defined Field
}field: The camelCase name of the field to filter on (e.g.,companyName,status,lastActivityDate). Always consult the official Autotask REST API documentation for the correct filterable field names for each entity.op: The filter operator. See Available Filter Operators.value: The value to compare against. For the'in'or'notin'operators, this should be an array (e.g.,[1, 2, 3]or['Val1', 'Val2']).udf: (Optional) Boolean, set totrueif thefieldis a User-Defined Field (UDF).
Multiple filters in the array are combined with AND logic.
Available Filter Operators (AutotaskApiFilterOp)
The AutotaskApiFilterOp type includes the following string literals. Refer to the Autotask REST API documentation for the definitive list of supported operators for specific fields and entities, as not all operators apply to all fields.
| Operator | Description | Example Value(s) | Notes |
|---|---|---|---|
eq | Equals | 1, "Active", true | |
noteq | Not equals | 1, "Active", true | Matches noteq in type def |
gt | Greater than | 100, "2023-01-01T00:00:00Z" | For numbers, dates |
gte | Greater than or equal to | 100, "2023-01-01" | For numbers, dates |
lt | Less than | 100, "2023-12-31" | For numbers, dates |
lte | Less than or equal to | 100, "2023-12-31" | For numbers, dates |
beginsWith | Starts with | "Tech" | Case-insensitive for strings |
endsWith | Ends with | "Solutions" | Case-insensitive for strings |
contains | Contains (substring match) | "Service" | Case-insensitive for strings |
exist | Field is not null | (value not applicable) | |
notExist | Field is null | (value not applicable) | |
in | In a list of values | [1, 2, 3], ["Val1", "Val2"] | value is an array |
notIn | Not in a list of values | [4, 5] | value is an array |
Important: Always cross-reference with the Autotask API documentation for the most accurate and up-to-date list of supported operators and their behavior for specific entity fields. The SDK's AutotaskQueryOperator type provides type safety for the operators listed above; the API may support additional query capabilities.
Filter Examples
// Active companies created this year
const recentActiveCompanies = await client.queryEntities("Companies", {
filter: [
{ field: "isActive", op: "eq", value: true },
{ field: "createDate", op: "isThisYear" },
],
fields: ["id", "companyName", "createDate"],
});
// Tickets with high priority, not yet completed
const highPriorityOpenTickets = await client.queryEntities("Tickets", {
filter: [
{ field: "priority", op: "eq", value: 1 }, // Assuming 1 is High Priority
{ field: "status", op: "notin", value: [5, 6] }, // Assuming 5=Complete, 6=Cancelled
{ field: "assignedResourceID", op: "isnotnull" }, // Must be assigned
],
fields: ["id", "ticketNumber", "title", "priority", "status"],
});Pagination
The queryEntities method uses a continuation token-based system.
Pagination Options
In AutotaskQueryOptions:
sdkPageSize?: number: Desired items per SDK page (default: 25). The SDK may make multiple API calls to fulfill this.continuationToken?: string: Token from a previous response to fetch the next/previous page.pageRequest?: 'next' | 'prev': Direction when usingcontinuationToken. Default is'next'.includeTotalCount?: boolean: Iftrue(and nocontinuationToken), makes an extra call for total count. Default:false.
When using continuationToken, the filter and fields from the original query are implicitly used. Re-specifying them might lead to unpredictable behavior.
Response Properties
SdkClientPagedResponse<T> contains:
items: T[]: Entities for the current page.pageSize: number:sdkPageSizeused.currentPage: number: 1-indexed SDK page number.continuationToken: string | null: Token for navigation. Null if no more pages in that direction.hasNextPage: boolean: If a next page is available.hasPreviousPage: boolean: If a previous page is available.totalItemsAvailable?: number: Total items matching (ifincludeTotalCountwas true on initial query).totalPages?: number: Total SDK pages (iftotalItemsAvailableknown).errors?: AutotaskErrorItem[]: API errors from the response body.
Example: Manual Pagination Loop
// Assume 'client' is an instantiated AutotaskClient
async function getAllActiveCompaniesManually() {
let allCompanies: AutotaskEntityTypeMap["Companies"][] = [];
let currentContinuationToken: string | null = null;
const sdkPageSize = 10;
let currentPageNumber = 0;
let totalItems: number | undefined;
let totalSdkPages: number | undefined;
const entityName: AllAutotaskEntityNames = "Companies";
console.log(`Fetching active companies, ${sdkPageSize} per SDK page...`);
try {
do {
currentPageNumber++;
const queryOptions: AutotaskQueryOptions = {
...(currentContinuationToken
? { continuationToken: currentContinuationToken } // Subsequent requests only need token
: {
// First request
fields: ["id", "companyName", "isActive"],
filter: [{ field: "isActive", op: "eq", value: true }],
includeTotalCount: true,
}),
sdkPageSize: sdkPageSize,
};
console.log(
`Requesting SDK page ${currentPageNumber}${
totalSdkPages ? ` of ${totalSdkPages}` : ""
}...`
);
const response = await client.queryEntities(entityName, queryOptions);
if (response.errors && response.errors.length > 0) {
console.error("Errors received:", response.errors);
break;
}
if (currentPageNumber === 1) {
// Capture totals from the first response
totalItems = response.totalItemsAvailable;
totalSdkPages = response.totalPages;
}
const pageInfo = totalSdkPages
? `SDK Page ${response.currentPage}/${totalSdkPages}`
: `SDK Page ${response.currentPage}`;
console.log(` Received ${pageInfo}: ${response.items.length} items.`);
response.items.forEach((company) => allCompanies.push(company));
currentContinuationToken = response.continuationToken;
} while (currentContinuationToken); // Continues as long as there's a token for the next page
console.log(
`\nSuccessfully fetched ${allCompanies.length} active companies.`
);
} catch (error) {
console.error("Error fetching all active companies:", error);
}
}
getAllActiveCompaniesManually();Fetching Related Entities (include Clause)
Fetch primary entities and their related entities using the include option in AutotaskQueryOptions.
Structure of IncludeClause
interface IncludeClause {
path: string; // Name of the relationship path (e.g., "Account" for a Ticket).
// MUST correspond to a defined relationship for the primary entity.
select?: string[]; // Optional: Fields to retrieve for the related entity.
}Relationship definitions are internally managed by the SDK (see relationships/index.ts).
How it Works
- Client fetches primary entities.
- For each
IncludeClause, it looks up the relationship configuration to findforeignEntity,localKey(on primary), andforeignKey(on related). - Collects
localKeyvalues from primary entities. - Makes additional API calls (using
queryAllEntitiesinternally) to fetch related entities, filtered by collected foreign key values. - Embeds related entities into primary entity objects under a property matching
path. - If
selectis used, only specified fields are on the embedded entity.
Current Limitation: Supports one level of relationship.
Example: Tickets with Account and Contact
// Assume 'client' is an instantiated AutotaskClient
// Assume 'Tickets' entity has relationships 'Account' (to 'Companies') and 'Contact' (to 'Contacts')
async function getTicketsWithDetails() {
try {
const options: AutotaskQueryOptions = {
filter: [{ field: "status", op: "eq", value: 1 }], // Example: Open tickets
fields: ["id", "title", "companyID", "contactID"],
include: [
{ path: "Account", select: ["id", "companyName"] },
{ path: "Contact", select: ["id", "firstName", "lastName"] },
],
sdkPageSize: 5,
};
const response = await client.queryEntities("Tickets", options);
response.items.forEach((ticket) => {
// Type assertion for included properties
const typedTicket = ticket as AutotaskEntityTypeMap["Tickets"] & {
Account?: Partial<AutotaskEntityTypeMap["Companies"]>;
Contact?: Partial<AutotaskEntityTypeMap["Contacts"]>;
};
console.log(`Ticket: ${typedTicket.title}`);
if (typedTicket.Account)
console.log(` Account: ${typedTicket.Account.companyName}`);
if (typedTicket.Contact)
console.log(
` Contact: ${typedTicket.Contact.firstName} ${typedTicket.Contact.lastName}`
);
});
if (response.errors)
console.warn("\nErrors during fetch/embedding:", response.errors);
} catch (error) {
console.error("Error fetching tickets with details:", error);
}
}
getTicketsWithDetails();Fetching Entity Field Definitions and Picklists
The SDK provides a method to retrieve detailed information about the fields of any Autotask entity, including their data types and picklist options. This is particularly useful for dynamically building UIs, validating data, or understanding entity structures programmatically.
The getEntityFieldsDetailed method on the AutotaskClient instance allows you to fetch this information.
import {
AutotaskClient,
ProcessedFieldDetailedInfo, // Make sure this type is imported
// ... other necessary imports
} from "@panoptic-it-solutions/autotask-client";
// Assume 'client' is an instantiated AutotaskClient
async function logTicketFieldDetails() {
try {
const entityName = "Tickets"; // Example: Get fields for the "Tickets" entity
// Fetch all fields for the "Tickets" entity
const allTicketFields: ProcessedFieldDetailedInfo[] =
await client.getEntityFieldsDetailed(entityName);
console.log(`Field details for ${entityName}:`);
allTicketFields.forEach(field => {
console.log(`- Field Name: ${field.name}`);
console.log(` Data Type: ${field.dataType}`);
console.log(` Is Picklist: ${field.isPickList}`);
if (field.isPickList && field.picklistMap) {
console.log(` Picklist Options (Value: Label):`);
for (const [value, label] of Object.entries(field.picklistMap)) {
console.log(` '${value}': '${label}'`);
}
}
// Example of accessing raw picklistValues if needed
// if (field.isPickList && Array.isArray(field.picklistValues)) {
// console.log(` Raw Picklist Values (first 2):`, field.picklistValues.slice(0, 2));
// }
});
// Example: Fetch specific fields for the "Companies" entity
const specificCompanyFields: ProcessedFieldDetailedInfo[] =
await client.getEntityFieldsDetailed("Companies", ["companyName", "companyType", "marketSegmentID"]);
console.log(`\nSpecific field details for Companies:`);
specificCompanyFields.forEach(field => {
console.log(`- Field: ${field.name}, Type: ${field.dataType}${field.isPickList ? ', Picklist' : ''}`);
if (field.name === "marketSegmentID" && field.picklistMap) {
console.log(` Market Segment Picklist:`, field.picklistMap);
}
});
} catch (error) {
console.error(`Error fetching field details:`, error);
}
}
logTicketFieldDetails();Understanding ProcessedFieldDetailedInfo
The getEntityFieldsDetailed method returns an array of ProcessedFieldDetailedInfo objects. Each object contains:
- All properties from
AutotaskRawField, which represents the raw data returned by the Autotask API for a field (e.g.,name,dataType,isPickList,isReadOnly,isRequired,picklistValuesarray). - An optional
picklistMap: IfisPickLististrueandpicklistValuesare available, this property provides a convenient key-value map where keys are the picklist item values (as strings) and values are their corresponding labels. This simplifies looking up display labels for picklist values.
For example, if a "Status" field (isPickList: true) has picklist values like { value: 1, label: "New" }, { value: 2, label: "In Progress" }, the picklistMap would be { "1": "New", "2": "In Progress" }.
Key Types and Interfaces
AllAutotaskEntityNames: Union type of all valid Autotask entity names (e.g.,"Companies").AutotaskEntityTypeMap: Maps entity names to their TypeScript interfaces (e.g.,AutotaskEntityTypeMap["Tickets"]isTicketEntityType).AutotaskQueryOptions: Options forqueryEntitiesandqueryAllEntities.AutotaskApiFilter,AutotaskApiFilterOp: For filter conditions.SdkClientPagedResponse<T>: Response fromqueryEntities.SdkProgressData<T>: Progress object forqueryAllEntitiescallback.IncludeClause: For defining related entities to embed.AutotaskClientConfig: Configuration forAutotaskClient.AutotaskRawField: Interface representing the raw field definition as returned by Autotask's/entityInformation/fieldsendpoint.ProcessedFieldDetailedInfo: ExtendsAutotaskRawFieldand includes an optionalpicklistMapfor easier consumption of picklist options. This is the return type forclient.getEntityFieldsDetailed().AutotaskErrorItem: Structure of errors in API responses.- Custom Error Classes (
AutotaskApiError,RateLimitError,NetworkError,TimeoutError,AutotaskClientError): Provide detailed error context.
These types are generally exported from @panoptic-it-solutions/autotask-client or accessible via its main exports. Refer to the SDK's types, autotask-types, and fields directories for detailed definitions.
Error Handling
- Wrap SDK calls in
try...catchblocks. - Check
response.errorsinSdkClientPagedResponsefor non-fatal API errors. - Handle specific custom error types for better diagnostics.
try {
const response = await client.queryEntities("Companies", {
/* ... */
});
if (response.errors && response.errors.length > 0) {
console.warn("API reported errors:", response.errors);
}
// Process response.items
} catch (error) {
if (error instanceof AutotaskApiError) {
console.error("Autotask API Error:", error.statusCode, error.errorsFromApi);
} else if (error instanceof RateLimitError) {
console.error("Rate limit hit (retries exhausted):", error.message);
} else {
console.error("SDK or Network Error:", error);
}
}Rate Limiting Details
- Uses Upstash Redis (if configured) for distributed rate limiting. Essential for serverless/scaled environments.
- Defaults (e.g., 5 req/sec, 5000 req/hr) are configurable in
AutotaskClientConfig.upstash. - Automatic retries with backoff for
RateLimitErroror API 429s. - Without Upstash, falls back to reactive retries on 429s (less robust).
Source Code Structure Overview
src/client.ts: MainAutotaskClientclass.src/types/: Core type definitions (config, API responses, errors).src/autotask-types/: Generated TypeScript interfaces for all Autotask entities.src/relationships/: Defines entity relationships for theincludefeature.src/client-internals/: Lower-level SDK mechanics (pagination, retry, request execution).src/rate-limiting/: Rate limiting implementation.
Platform-Specific Configuration Examples
How you configure and use the AutotaskClient can vary. Using Upstash Redis for rate limiting is strongly recommended for all production and serverless/scaled environments.
1. Standard Node.js (e.g., Express.js Server)
Instantiate AutotaskClient once (singleton pattern) and reuse.
// src/services/autotaskService.ts
import {
AutotaskClient,
AutotaskClientConfig,
LogLevel,
} from "@panoptic-it-solutions/autotask-client";
import * as dotenv from "dotenv";
dotenv.config();
const clientConfig: AutotaskClientConfig = {
userName: process.env.AUTOTASK_USERNAME!,
secret: process.env.AUTOTASK_SECRET!,
integrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
apiBaseUrl: process.env.AUTOTASK_API_URL!,
upstash: {
upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL,
upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN,
},
logLevel: LogLevel.INFO,
};
const autotaskClient = new AutotaskClient(clientConfig);
export default autotaskClient;
// Usage in an Express route
// import autotaskClient from '../services/autotaskService';
// router.get('/companies', async (req, res) => { /* ... use autotaskClient ... */ });2. Next.js / Vercel (Serverless Functions)
Instantiate AutotaskClient inside your API route handler. Upstash Redis is critical here.
// pages/api/autotask/contacts.ts
import type { NextApiRequest, NextApiResponse } from "next";
import {
AutotaskClient,
AutotaskClientConfig /* other types */,
} from "@panoptic-it-solutions/autotask-client";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === "GET") {
try {
const clientConfig: AutotaskClientConfig = {
userName: process.env.AUTOTASK_USERNAME!,
secret: process.env.AUTOTASK_SECRET!,
integrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
apiBaseUrl: process.env.AUTOTASK_API_URL!,
upstash: {
// Essential for serverless
upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL,
upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN,
},
};
const client = new AutotaskClient(clientConfig);
// ... use client to fetch data ...
// res.status(200).json(contactData);
} catch (error: any) {
console.error("Autotask API Error:", error);
res.status(500).json({ error: error.message || "Failed to fetch" });
}
} // ...
}3. Other Serverless Environments (e.g., AWS Lambda, Google Cloud Functions)
Principles are similar: instantiate client in handler, use distributed rate limiting (Upstash).
// Example for AWS Lambda
import {
AutotaskClient,
AutotaskClientConfig /* other types */,
} from "@panoptic-it-solutions/autotask-client";
export const handler = async (event: any) => {
try {
const clientConfig: AutotaskClientConfig = {
userName: process.env.AUTOTASK_USERNAME!,
// ... other config from environment variables ...
upstash: {
upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL,
upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN,
},
};
const client = new AutotaskClient(clientConfig);
// ... use client ...
// return { statusCode: 200, body: JSON.stringify(data) };
} catch (error: any) {
// ... error handling ...
}
};Rules for AI
We have created instructions and best practices for AI assistants when working with the @@panoptic-it-solutions/autotask-client SDK in a codebase. To view these rules open the gist here
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
License
This project is licensed under the AGPLv3 License. See the LICENSE file for details.
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago