@applyguy/sdk 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Apply Guy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,336 @@
1
- # Temporary Holding Version
1
+ # Apply Guy TypeScript SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![CI](https://github.com/ApplyGuy/typescript-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/ApplyGuy/typescript-sdk/actions/workflows/ci.yml)
4
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+
6
+ The official, zero-runtime-dependency TypeScript client for the
7
+ [Apply Guy Developer API](https://applyguy.ai/developers). Search fresh U.S.
8
+ jobs, manage a candidate profile, and submit applications through pure HTTP.
9
+
10
+ ## Requirements
11
+
12
+ - Node.js 18 or newer
13
+ - An Apply Guy developer API key
14
+
15
+ This is an ESM package and uses the native Fetch, Blob, and FormData APIs.
16
+
17
+ ## Install
18
+
19
+ ~~~bash
20
+ npm install github:ApplyGuy/typescript-sdk#v0.2.0
21
+ ~~~
22
+
23
+ ## Platforms with multiple candidates
24
+
25
+ Use a single API key and credit balance for your platform. Each candidate has a stable ID and their own profile, preferences, files and mailbox. Existing integrations can continue using the account profile without a candidate ID.
26
+
27
+ ```ts
28
+ const { data: candidate } = await client.candidates.create({
29
+ externalId: user.id, candidateContext: user.reviewedProfile,
30
+ });
31
+ const { data: resume } = await client.candidates.uploadDocument(candidate.id, "resume", resumePdf, "resume.pdf");
32
+ await client.candidates.update(candidate.id, { defaultResumeId: resume.id });
33
+ const { data: letter } = await client.candidates.uploadDocument(candidate.id, "cover_letter", letterPdf, "cover-letter.pdf");
34
+ const input = { candidateId: candidate.id, jobId, resumeId: resume.id, coverLetterId: letter.id, mode: "agent" as const };
35
+ const { data: check } = await client.applications.preflight(input);
36
+ if (check.ready) await client.applications.create(input, savedApplicationIntentKey);
37
+ ```
38
+
39
+ Upload PDF, DOC or DOCX up to 5 MB. Store the returned IDs. Omit `resumeId` to use the candidate's default. Switching defaults and editing a profile affect future applications. A queued application keeps its original profile and files throughout screening and retries. Cover-letter files are supported for Ashby, BambooHR, Paylocity, Rippling and Workable when the form has a cover-letter upload. Other ATS integrations return `cover_letter_unsupported` if you supply one.
40
+
41
+ For inbox verification, call `client.candidates.connectEmail(candidate.id, { gmailUser, gmailAppPassword })` with the candidate's own Gmail or Google Workspace mailbox. Changing their email disconnects that mailbox. Workday requires a connected inbox. Greenhouse may request verification during submission. Check preflight and the final application’s `outcomeCode` for `email_integration_required` or `email_verification_failed`.
42
+
43
+ Use `client.applications.list({ candidateId })` or signed webhook events to track each candidate. Agent-mode answer requests and credit reservations use the same API as single-profile applications. Reusing an idempotency key with a different candidate or document override returns a conflict. Archiving a candidate closes it to new applications and preserves its application history.
44
+
45
+ ## Agent-first CLI
46
+
47
+ The package also installs an `applyguy` executable. It is designed for coding
48
+ agents and unattended workflows: every success is one JSON document on stdout,
49
+ every failure is one JSON document on stderr, and it never prompts or emits
50
+ terminal colors.
51
+
52
+ Install it globally from GitHub today:
53
+
54
+ ~~~bash
55
+ npm install --global github:ApplyGuy/typescript-sdk
56
+ export APPLYGUY_API_KEY="your_api_key"
57
+
58
+ applyguy jobs search --q "software engineer" --remote remote --limit 25
59
+ applyguy credits balance
60
+ ~~~
61
+
62
+ After the npm release, the install command is `npm install --global
63
+ @applyguy/sdk`.
64
+
65
+ Metered commands return their exact credit usage in `meta.creditUsage`. Job
66
+ search and job detail are metered; application preflight is free. Creating an
67
+ application submits to a real employer, so agents should preflight first and
68
+ must provide an explicit idempotency key:
69
+
70
+ ~~~bash
71
+ applyguy applications preflight --job-id JOB_ID --mode agent
72
+
73
+ applyguy applications create \
74
+ --job-id JOB_ID \
75
+ --mode agent \
76
+ --idempotency-key "workflow-123:JOB_ID" \
77
+ --wait
78
+ ~~~
79
+
80
+ `--wait` exits when the application succeeds, fails, is cancelled, or needs an
81
+ answer from your agent. In agent mode, retrieve the exact request and return a
82
+ JSON payload through stdin:
83
+
84
+ ~~~bash
85
+ applyguy applications answer-request APPLICATION_ID
86
+ applyguy applications answer APPLICATION_ID --input - < answer.json
87
+ applyguy applications wait APPLICATION_ID --require-success
88
+ ~~~
89
+
90
+ Complex and sensitive request bodies use `--input <file|->`. Secrets are read
91
+ only from environment variables or files, never command-line arguments:
92
+
93
+ - `APPLYGUY_API_KEY` or `APPLYGUY_API_KEY_FILE`
94
+ - `APPLYGUY_ACCESS_TOKEN` or `APPLYGUY_ACCESS_TOKEN_FILE`
95
+ - `APPLYGUY_REFRESH_TOKEN` or `APPLYGUY_REFRESH_TOKEN_FILE`
96
+
97
+ Run `applyguy --help` for the complete command surface. Stable exit codes make
98
+ failures straightforward for agents to classify:
99
+
100
+ | Exit | Meaning |
101
+ | ---: | --- |
102
+ | 0 | Success, including a non-success application status unless `--require-success` is set |
103
+ | 2 | Invalid command or input |
104
+ | 3 | Authentication or credential error |
105
+ | 4 | Resource not found |
106
+ | 5 | Conflict or application/business failure |
107
+ | 6 | Insufficient credits |
108
+ | 7 | Rate limited |
109
+ | 8 | API service or network failure |
110
+ | 9 | Wait timed out; the remote application was not cancelled |
111
+ | 10 | Unexpected local error |
112
+ | 130 | Interrupted; the remote application was not cancelled |
113
+
114
+ ## Quick start
115
+
116
+ ~~~ts
117
+ import { ApplyGuy } from "@applyguy/sdk";
118
+
119
+ const applyGuy = new ApplyGuy({
120
+ apiKey: process.env.APPLYGUY_API_KEY!,
121
+ });
122
+
123
+ const search = await applyGuy.jobs.search({
124
+ q: "software engineer",
125
+ remote: "remote",
126
+ limit: 25,
127
+ });
128
+
129
+ console.log(search.data);
130
+ console.log(search.creditUsage?.cost); // 0.05
131
+ console.log(search.creditUsage?.balance);
132
+ ~~~
133
+
134
+ ## Submit a managed application
135
+
136
+ Managed mode costs 2 credits. Apply Guy discovers the employer form, resolves
137
+ its questions from the stored candidate profile, and submits it.
138
+
139
+ ~~~ts
140
+ const job = search.data[0];
141
+ if (!job) throw new Error("No matching job found.");
142
+
143
+ const preflight = await applyGuy.applications.preflight({
144
+ jobId: job.id,
145
+ mode: "managed",
146
+ });
147
+
148
+ if (!preflight.data.ready) {
149
+ throw new Error(
150
+ `Application is not ready: ${preflight.data.missingProfileFields.join(", ")}`,
151
+ );
152
+ }
153
+
154
+ const queued = await applyGuy.applications.create(
155
+ { jobId: job.id, mode: "managed" },
156
+ crypto.randomUUID(),
157
+ );
158
+
159
+ console.log(queued.data.id, queued.data.status);
160
+ ~~~
161
+
162
+ The second argument is an idempotency key. Reusing it for the same request
163
+ returns the original application instead of charging twice.
164
+
165
+ ## Bring your own answering agent
166
+
167
+ Agent mode costs 1 credit. Apply Guy handles form discovery, uploads, ATS
168
+ sessions, and submission while your agent generates the structured answers.
169
+ An application may request more than one answer round.
170
+
171
+ ~~~ts
172
+ const queued = await applyGuy.applications.create(
173
+ { jobId: job.id, mode: "agent" },
174
+ crypto.randomUUID(),
175
+ );
176
+
177
+ while (true) {
178
+ const { data: application } = await applyGuy.applications.get(queued.data.id);
179
+
180
+ if (application.status === "success" || application.status === "failed") {
181
+ console.log(application.status, application.outcomeCode);
182
+ break;
183
+ }
184
+
185
+ if (application.status === "awaiting_answers") {
186
+ const { data: request } =
187
+ await applyGuy.applications.answerRequest(application.id);
188
+
189
+ // Call your model with request.prompt and constrain its output to
190
+ // request.responseSchema.
191
+ const answer = await yourAgent.generate({
192
+ prompt: request.prompt,
193
+ responseSchema: request.responseSchema,
194
+ });
195
+
196
+ await applyGuy.applications.answer(application.id, {
197
+ requestId: request.id,
198
+ fingerprint: request.fingerprint,
199
+ answer,
200
+ });
201
+ }
202
+
203
+ await new Promise((resolve) => setTimeout(resolve, 2_000));
204
+ }
205
+ ~~~
206
+
207
+ The server validates the answer against the exact JSON Schema it supplied.
208
+ Malformed answers fail immediately instead of reaching an employer.
209
+
210
+ ## Create an account and API key programmatically
211
+
212
+ The bootstrap endpoint creates an account and returns the first API key plus
213
+ short-lived account tokens. Payment remains the only required human step.
214
+
215
+ ~~~ts
216
+ const bootstrap = await ApplyGuy.bootstrap({
217
+ email: "developer@example.com",
218
+ password: process.env.APPLYGUY_ACCOUNT_PASSWORD!,
219
+ name: "Production agent",
220
+ });
221
+
222
+ const applyGuy = new ApplyGuy({
223
+ apiKey: bootstrap.apiKey.key,
224
+ accessToken: bootstrap.accessToken,
225
+ });
226
+ ~~~
227
+
228
+ The API key and refresh token are returned once. Store them in a secret
229
+ manager. The account access token is used for API-key and integration
230
+ management; normal `/v1` operations use the developer API key.
231
+
232
+ ## Candidate profile and resume
233
+
234
+ ~~~ts
235
+ import { readFile } from "node:fs/promises";
236
+
237
+ await applyGuy.profile.update({
238
+ firstName: "Jordan",
239
+ lastName: "Bamber",
240
+ email: "jordan@example.com",
241
+ phone: "+1 415 555 0199",
242
+ location: "San Francisco, CA",
243
+ address: {
244
+ line1: "501 Delancey Street",
245
+ city: "San Francisco",
246
+ state: "California",
247
+ postalCode: "94107",
248
+ country: "United States of America",
249
+ },
250
+ workAuthorization: {
251
+ country: "US",
252
+ status: "citizen",
253
+ requiresSponsorship: false,
254
+ },
255
+ });
256
+
257
+ const resume = await readFile("./resume.pdf");
258
+ await applyGuy.profile.uploadResume(
259
+ new Blob([resume], { type: "application/pdf" }),
260
+ "resume.pdf",
261
+ );
262
+ ~~~
263
+
264
+ ## Errors and retries
265
+
266
+ Non-2xx responses throw `ApplyGuyApiError` with the public error code,
267
+ structured details, request ID, and parsed Retry-After delay when present.
268
+
269
+ ~~~ts
270
+ import { ApplyGuyApiError } from "@applyguy/sdk";
271
+
272
+ try {
273
+ await applyGuy.applications.create(
274
+ { jobUrl: "https://boards.greenhouse.io/example/jobs/123", mode: "managed" },
275
+ crypto.randomUUID(),
276
+ );
277
+ } catch (error) {
278
+ if (error instanceof ApplyGuyApiError) {
279
+ console.error(error.status, error.code, error.requestId, error.details);
280
+ if (error.retryAfterSeconds !== undefined) {
281
+ console.error(`Retry in ${error.retryAfterSeconds} seconds`);
282
+ }
283
+ }
284
+ }
285
+ ~~~
286
+
287
+ ## Credits
288
+
289
+ - 20 credits per U.S. dollar
290
+ - Managed application: 2 credits
291
+ - Bring-your-own-agent application: 1 credit
292
+ - Job search: 0.05 credit per requested block of up to 25 results
293
+ - Job detail: 0.01 credit when the request returns 200
294
+ - Application preflight: free
295
+
296
+ Successful metered job reads expose decimal and exact milli-credit values on
297
+ `creditUsage`. Invalid requests, missing jobs, rate limits, and server errors
298
+ are not charged.
299
+
300
+ ~~~ts
301
+ const { data: credits } = await applyGuy.credits.get();
302
+ console.log(credits.balance, credits.balanceMilli);
303
+
304
+ const checkout = await applyGuy.credits.checkout(2_500); // $25.00
305
+ console.log(checkout.data.checkoutUrl);
306
+ ~~~
307
+
308
+ ## API surface
309
+
310
+ - `jobs`: search and job detail
311
+ - `profile`: read/update candidate context and upload a resume
312
+ - `credits`: balance, pricing, checkout, and checkout status
313
+ - `applications`: preflight, create, list, poll, answer, and cancel
314
+ - `webhooks`: list, create, update, and delete endpoints
315
+ - `usage`: analytics and revocable share links
316
+ - `apiKeys`: list, create, and revoke keys using an account access token
317
+ - `integrations`: inspect and configure the Workday Gmail integration
318
+
319
+ See the complete generated
320
+ [OpenAPI reference](https://applyguy.ai/developers/docs/reference) and
321
+ [developer guides](https://applyguy.ai/developers/docs).
322
+
323
+ ## Security
324
+
325
+ Use this SDK only in trusted server-side code. Never expose an Apply Guy API
326
+ key, account access token, Gmail app password, candidate profile, or resume in
327
+ a browser bundle or public repository.
328
+
329
+ Custom remote base URLs must use HTTPS. Plain HTTP is accepted only for
330
+ loopback development hosts.
331
+
332
+ Please report vulnerabilities as described in [SECURITY.md](SECURITY.md).
333
+
334
+ ## License
335
+
336
+ [MIT](LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}