From 4c46968b77a18546337ebc84236cd7173f7d7372 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:54:37 -0700 Subject: [PATCH 1/4] feat: create/delete/enable/disable API keys --- CHANGELOG.md | 8 + package.json | 2 +- src/services/api_key_service.js | 89 ++- .../recording.har | 587 ++++++++++++++++++ test/services/api_key.test.js | 39 +- 5 files changed, 704 insertions(+), 21 deletions(-) create mode 100644 test/cassettes/ApiKey-Service_1820824481/creates-disables-enables-and-deletes-an-API-key_2354899526/recording.har diff --git a/CHANGELOG.md b/CHANGELOG.md index ee323ee6c..744104662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG +## v8.5.0 (2026-02-03) + +- Adds the following functions usable by child and referral customer users: + - `ApiKey.create` + - `ApiKey.delete` + - `ApiKey.enable` + - `ApiKey.disable` + ## v8.4.0 (2025-11-24) - Adds the following functions: diff --git a/package.json b/package.json index 52ed2a052..97b410d7a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@easypost/api", "description": "EasyPost Node Client Library", - "version": "8.4.0", + "version": "8.5.0", "author": "Easypost Engineering ", "homepage": "https://easypost.com", "exports": { diff --git a/src/services/api_key_service.js b/src/services/api_key_service.js index e798faf2b..20f1d1b63 100644 --- a/src/services/api_key_service.js +++ b/src/services/api_key_service.js @@ -10,17 +10,6 @@ export default (easypostClient) => * @param {EasyPostClient} easypostClient - The pre-configured EasyPostClient instance to use for API requests with this service. */ class ApiKeyService extends baseService(easypostClient) { - /** - * Retrieve all {@link ApiKey API keys} associated with the current authenticated user. - * See {@link https://docs.easypost.com/docs/api-keys#retrieve-an-api-key EasyPost API Documentation} for more information. - * @returns {Object} - An object containing the API keys associated with the current authenticated user and its child users. - */ - static async all(params = {}) { - const url = 'api_keys'; - - return this._all(url, params); - } - /** * Retrieve API Keys for a specified {@link User user}. * See {@link https://docs.easypost.com/docs/api-keys#retrieve-an-api-key EasyPost API Documentation} for more information. @@ -50,4 +39,82 @@ export default (easypostClient) => throw new FilteringError({ message: util.format(Constants.NO_OBJECT_FOUND, 'child') }); } + + /** + * Retrieve all {@link ApiKey API keys} associated with the current authenticated user. + * See {@link https://docs.easypost.com/docs/api-keys#retrieve-an-api-key EasyPost API Documentation} for more information. + * @returns {Object} - An object containing the API keys associated with the current authenticated user and its child users. + */ + static async all(params = {}) { + const url = 'api_keys'; + + return this._all(url, params); + } + + /** + * Create an API key for a child or referral customer user. + * See {@link https://docs.easypost.com/docs/api-keys#create-an-api-key EasyPost API Documentation} for more information. + * @param {string} mode - The mode for the API key (either "production" or "test"). + * @returns {ApiKey} - The created API key. + */ + static async create(mode) { + const url = 'api_keys'; + const params = { mode }; + + return this._create(url, params); + } + + /** + * Delete an API key for a child or referral customer user. + * See {@link https://docs.easypost.com/docs/api-keys#delete-an-api-key EasyPost API Documentation} for more information. + * @param {string} id - The ID of the API key to delete. + * @returns {Promise|Promise} - A promise that resolves if the API key was successfully deleted. + */ + static async delete(id) { + const url = `api_keys/${id}`; + + try { + await easypostClient._delete(url); + + return Promise.resolve(); + } catch (e) { + return Promise.reject(e); + } + } + + /** + * Enable a child or referral customer API key. + * See {@link https://docs.easypost.com/docs/api-keys#enable-an-api-key EasyPost API Documentation} for more information. + * @param {string} id - The ID of the API key to enable. + * @returns {ApiKey} - The enabled API key. + */ + static async enable(id) { + const url = `api_keys/${id}/enable`; + + try { + const response = await easypostClient._post(url); + + return this._convertToEasyPostObject(response.body); + } catch (e) { + return Promise.reject(e); + } + } + + /** + * Disable a child or referral customer API key. + * See {@link https://docs.easypost.com/docs/api-keys#disable-an-api-key EasyPost API Documentation} for more information. + * @param {string} id - The ID of the API key to disable. + * @returns {ApiKey} - The disabled API key. + */ + static async disable(id) { + const url = `api_keys/${id}/disable`; + + try { + const response = await easypostClient._post(url); + + return this._convertToEasyPostObject(response.body); + } catch (e) { + return Promise.reject(e); + } + } }; diff --git a/test/cassettes/ApiKey-Service_1820824481/creates-disables-enables-and-deletes-an-API-key_2354899526/recording.har b/test/cassettes/ApiKey-Service_1820824481/creates-disables-enables-and-deletes-an-API-key_2354899526/recording.har new file mode 100644 index 000000000..319ca613f --- /dev/null +++ b/test/cassettes/ApiKey-Service_1820824481/creates-disables-enables-and-deletes-an-API-key_2354899526/recording.har @@ -0,0 +1,587 @@ +{ + "log": { + "_recordingName": "ApiKey Service/creates, disables, enables, and deletes an API key", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "88943f478714367a3a4dea4e2afadd88", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 21, + "cookies": [], + "headers": [ + { + "name": "accept-encoding", + "value": "gzip, deflate" + }, + { + "name": "accept", + "value": "application/json" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": 21 + }, + { + "name": "host", + "value": "api.easypost.com" + } + ], + "headersSize": 389, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"mode\":\"production\"}" + }, + "queryString": [], + "url": "https://api.easypost.com/v2/api_keys" + }, + "response": { + "bodySize": 264, + "content": { + "encoding": "base64", + "mimeType": "application/json; charset=utf-8", + "size": 264, + "text": "{\"object\":\"ApiKey\",\"key\":\"\",\"mode\":\"production\",\"created_at\":\"2026-02-02T21:53:27Z\",\"active\":true,\"id\":\"ak_0244541a97eb465fa3e783ef44c842f5\"}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "x-download-options", + "value": "noopen" + }, + { + "name": "x-permitted-cross-domain-policies", + "value": "none" + }, + { + "name": "referrer-policy", + "value": "strict-origin-when-cross-origin" + }, + { + "name": "x-ep-request-uuid", + "value": "cfd5bd4169811cd7e2bbb17d028cf82c" + }, + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "x-runtime", + "value": "0.099897" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "x-node", + "value": "bigweb53nuq" + }, + { + "name": "x-version-label", + "value": "easypost-202602022023-e2aafa4ad5-master" + }, + { + "name": "x-backend", + "value": "easypost" + }, + { + "name": "x-proxied", + "value": "intlb3nuq d9379ca146, extlb1nuq cbbd141214" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 201, + "statusText": "Created" + }, + "startedDateTime": "2026-02-02T21:53:27.696Z", + "time": 233, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 233 + } + }, + { + "_id": "9d730cbb171d11c900e0c62dfa065985", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 2, + "cookies": [], + "headers": [ + { + "name": "accept-encoding", + "value": "gzip, deflate" + }, + { + "name": "accept", + "value": "application/json" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": 2 + }, + { + "name": "host", + "value": "api.easypost.com" + } + ], + "headersSize": 432, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{}" + }, + "queryString": [], + "url": "https://api.easypost.com/v2/api_keys/ak_0244541a97eb465fa3e783ef44c842f5/disable" + }, + "response": { + "bodySize": 264, + "content": { + "encoding": "base64", + "mimeType": "application/json; charset=utf-8", + "size": 264, + "text": "{\"object\":\"ApiKey\",\"key\":\"\",\"mode\":\"production\",\"created_at\":\"2026-02-02T21:53:27Z\",\"active\":false,\"id\":\"ak_0244541a97eb465fa3e783ef44c842f5\"}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "x-download-options", + "value": "noopen" + }, + { + "name": "x-permitted-cross-domain-policies", + "value": "none" + }, + { + "name": "referrer-policy", + "value": "strict-origin-when-cross-origin" + }, + { + "name": "x-ep-request-uuid", + "value": "cfd5bd4469811cd8e2bbb17e028cf864" + }, + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "x-runtime", + "value": "0.212678" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "x-node", + "value": "bigweb33nuq" + }, + { + "name": "x-version-label", + "value": "easypost-202602022023-e2aafa4ad5-master" + }, + { + "name": "x-backend", + "value": "easypost" + }, + { + "name": "x-proxied", + "value": "intlb3nuq d9379ca146, extlb1nuq cbbd141214" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2026-02-02T21:53:27.934Z", + "time": 331, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 331 + } + }, + { + "_id": "b2be59f01700874d648212aca67136c7", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 2, + "cookies": [], + "headers": [ + { + "name": "accept-encoding", + "value": "gzip, deflate" + }, + { + "name": "accept", + "value": "application/json" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": 2 + }, + { + "name": "host", + "value": "api.easypost.com" + } + ], + "headersSize": 431, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{}" + }, + "queryString": [], + "url": "https://api.easypost.com/v2/api_keys/ak_0244541a97eb465fa3e783ef44c842f5/enable" + }, + "response": { + "bodySize": 264, + "content": { + "encoding": "base64", + "mimeType": "application/json; charset=utf-8", + "size": 264, + "text": "{\"object\":\"ApiKey\",\"key\":\"\",\"mode\":\"production\",\"created_at\":\"2026-02-02T21:53:27Z\",\"active\":true,\"id\":\"ak_0244541a97eb465fa3e783ef44c842f5\"}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "x-download-options", + "value": "noopen" + }, + { + "name": "x-permitted-cross-domain-policies", + "value": "none" + }, + { + "name": "referrer-policy", + "value": "strict-origin-when-cross-origin" + }, + { + "name": "x-ep-request-uuid", + "value": "cfd5bd3d69811cd8e2bbb180028cf8e2" + }, + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "x-runtime", + "value": "0.151919" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "x-node", + "value": "bigweb38nuq" + }, + { + "name": "x-version-label", + "value": "easypost-202602022023-e2aafa4ad5-master" + }, + { + "name": "x-backend", + "value": "easypost" + }, + { + "name": "x-proxied", + "value": "intlb4nuq d9379ca146, extlb1nuq cbbd141214" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2026-02-02T21:53:28.268Z", + "time": 276, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 276 + } + }, + { + "_id": "bfbd56e1b1f10f7a8fef8676f367f0b7", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept-encoding", + "value": "gzip, deflate" + }, + { + "name": "accept", + "value": "application/json" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "host", + "value": "api.easypost.com" + } + ], + "headersSize": 407, + "httpVersion": "HTTP/1.1", + "method": "DELETE", + "queryString": [], + "url": "https://api.easypost.com/v2/api_keys/ak_0244541a97eb465fa3e783ef44c842f5" + }, + "response": { + "bodySize": 0, + "content": { + "mimeType": "text/plain", + "size": 0 + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "x-download-options", + "value": "noopen" + }, + { + "name": "x-permitted-cross-domain-policies", + "value": "none" + }, + { + "name": "referrer-policy", + "value": "strict-origin-when-cross-origin" + }, + { + "name": "x-ep-request-uuid", + "value": "cfd5bd4469811cd8e2bbb491028cf946" + }, + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "x-runtime", + "value": "0.141285" + }, + { + "name": "x-node", + "value": "bigweb36nuq" + }, + { + "name": "x-version-label", + "value": "easypost-202602022023-e2aafa4ad5-master" + }, + { + "name": "x-backend", + "value": "easypost" + }, + { + "name": "x-proxied", + "value": "intlb3nuq d9379ca146, extlb1nuq cbbd141214" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 611, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 204, + "statusText": "No Content" + }, + "startedDateTime": "2026-02-02T21:53:28.547Z", + "time": 258, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 258 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/test/services/api_key.test.js b/test/services/api_key.test.js index 4a8b427a4..fd250fc55 100644 --- a/test/services/api_key.test.js +++ b/test/services/api_key.test.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import EasyPostClient from '../../src/easypost'; +import FilteringError from '../../src/errors/general/filtering_error'; import ApiKey from '../../src/models/api_key'; import * as setupPolly from '../helpers/setup_polly'; -import FilteringError from '../../src/errors/general/filtering_error'; describe('ApiKey Service', function () { const getPolly = setupPolly.setupPollyTests(); @@ -19,14 +19,6 @@ describe('ApiKey Service', function () { setupPolly.setupCassette(server); }); - it('retrieves all apiKeys', async function () { - const apiKeys = await client.ApiKey.all(); - - apiKeys.keys.forEach((apiKey) => { - expect(apiKey).to.be.an.instanceOf(ApiKey); - }); - }); - it("retrieves parent user's API keys", async function () { const user = await client.User.retrieveMe(); const keys = await client.ApiKey.retrieveApiKeysForUser(user.id); @@ -44,4 +36,33 @@ describe('ApiKey Service', function () { expect(error).to.be.an.instanceOf(FilteringError, 'No child found.'); } }); + + it('retrieves all apiKeys', async function () { + const apiKeys = await client.ApiKey.all(); + + apiKeys.keys.forEach((apiKey) => { + expect(apiKey).to.be.an.instanceOf(ApiKey); + }); + }); + + it('creates, disables, enables, and deletes an API key', async function () { + const referralClient = new EasyPostClient(process.env.REFERRAL_CUSTOMER_PROD_API_KEY); + + // Create an API key + const apiKey = await referralClient.ApiKey.create('production'); + expect(apiKey).to.be.an.instanceOf(ApiKey); + expect(apiKey.id).to.match(/^ak_/); + expect(apiKey.mode).to.equal('production'); + + // Disable the API key + const disabledApiKey = await referralClient.ApiKey.disable(apiKey.id); + expect(disabledApiKey.active).to.equal(false); + + // Enable the API key + const enabledApiKey = await referralClient.ApiKey.enable(apiKey.id); + expect(enabledApiKey.active).to.equal(true); + + // Delete the API key + await referralClient.ApiKey.delete(apiKey.id); + }); }); From 14a3bae06a533f228a9b177caeeee8a08283943f Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Mon, 2 Feb 2026 15:32:58 -0700 Subject: [PATCH 2/4] feat: typescript definitions for new functions --- types/ApiKey/ApiKey.d.ts | 56 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/types/ApiKey/ApiKey.d.ts b/types/ApiKey/ApiKey.d.ts index 0d41af79c..fbb1bb2ce 100644 --- a/types/ApiKey/ApiKey.d.ts +++ b/types/ApiKey/ApiKey.d.ts @@ -26,13 +26,63 @@ export declare class ApiKey implements IApiKey { active: boolean; /** + * Retrieve API Keys for a specified User. * Both production and test keys will be returned for a User and all of its children. * If the request is authenticated as a Child, only the API Keys for that Child will be returned. * * @see https://docs.easypost.com/docs/api-keys#retrieve-an-api-key * - * @param apiKeyId Unique, begins with "user_" - * @returns {Promise} The verified {@link ApiKey}. + * @param {string} id - The ID of the user to retrieve keys for. + * @returns {Promise} - List of associated API Keys. */ - static retrieve(apiKeyId: string): Promise; + static retrieveApiKeysForUser(id: string): Promise; + + /** + * Retrieve all API keys associated with the current authenticated user. + * + * @see https://docs.easypost.com/docs/api-keys#retrieve-an-api-key + * + * @param {Object} [params] - Optional parameters for the request. + * @returns {Promise} - An object containing the API keys associated with the current authenticated user and its child users. + */ + static all(params?: Object): Promise; + + /** + * Create an API key for a child or referral customer user. + * + * @see https://docs.easypost.com/docs/api-keys#create-an-api-key + * + * @param {string} mode - The mode for the API key (either "production" or "test"). + * @returns {Promise} - The created API key. + */ + static create(mode: string): Promise; + + /** + * Delete an API key for a child or referral customer user. + * + * @see https://docs.easypost.com/docs/api-keys#delete-an-api-key + * + * @param {string} id - The ID of the API key to delete. + */ + static delete(id: string): void; + + /** + * Enable a child or referral customer API key. + * + * @see https://docs.easypost.com/docs/api-keys#enable-an-api-key + * + * @param {string} id - The ID of the API key to enable. + * @returns {Promise} - The enabled API key. + */ + static enable(id: string): Promise; + + /** + * Disable a child or referral customer API key. + * + * @see https://docs.easypost.com/docs/api-keys#disable-an-api-key + * + * @param {string} id - The ID of the API key to disable. + * @returns {Promise} - The disabled API key. + */ + static disable(id: string): Promise; } From 96db091b296adb8b99843717bb8566dfd274a44f Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:54:38 -0700 Subject: [PATCH 3/4] chore: trigger GitHub Actions From 5428370af6d102b2fe7101513744064e033d6181 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:57:36 -0700 Subject: [PATCH 4/4] fix: fallback env var for tests --- test/services/api_key.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/services/api_key.test.js b/test/services/api_key.test.js index fd250fc55..de4d843bb 100644 --- a/test/services/api_key.test.js +++ b/test/services/api_key.test.js @@ -46,7 +46,7 @@ describe('ApiKey Service', function () { }); it('creates, disables, enables, and deletes an API key', async function () { - const referralClient = new EasyPostClient(process.env.REFERRAL_CUSTOMER_PROD_API_KEY); + const referralClient = new EasyPostClient(process.env.REFERRAL_CUSTOMER_PROD_API_KEY || '123'); // Create an API key const apiKey = await referralClient.ApiKey.create('production');