jobber-mcp-server 1.0.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/.env.example +16 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/auth/oauth.d.ts +19 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +94 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/graphql/queries.d.ts +32 -0
- package/dist/graphql/queries.d.ts.map +1 -0
- package/dist/graphql/queries.js +447 -0
- package/dist/graphql/queries.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/clients.d.ts +6 -0
- package/dist/tools/clients.d.ts.map +1 -0
- package/dist/tools/clients.js +186 -0
- package/dist/tools/clients.js.map +1 -0
- package/dist/tools/invoices.d.ts +6 -0
- package/dist/tools/invoices.d.ts.map +1 -0
- package/dist/tools/invoices.js +64 -0
- package/dist/tools/invoices.js.map +1 -0
- package/dist/tools/jobs.d.ts +6 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +205 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/quotes.d.ts +6 -0
- package/dist/tools/quotes.d.ts.map +1 -0
- package/dist/tools/quotes.js +113 -0
- package/dist/tools/quotes.js.map +1 -0
- package/dist/tools/requests.d.ts +6 -0
- package/dist/tools/requests.d.ts.map +1 -0
- package/dist/tools/requests.js +117 -0
- package/dist/tools/requests.js.map +1 -0
- package/dist/tools/scheduling.d.ts +6 -0
- package/dist/tools/scheduling.d.ts.map +1 -0
- package/dist/tools/scheduling.js +197 -0
- package/dist/tools/scheduling.js.map +1 -0
- package/dist/transport.d.ts +7 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +75 -0
- package/dist/transport.js.map +1 -0
- package/dist/utils/errors.d.ts +42 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +72 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/pagination.d.ts +29 -0
- package/dist/utils/pagination.d.ts.map +1 -0
- package/dist/utils/pagination.js +49 -0
- package/dist/utils/pagination.js.map +1 -0
- package/package.json +55 -0
- package/scripts/extract-tokens.py +46 -0
package/.env.example
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Jobber OAuth2 App Credentials
|
|
2
|
+
# Get these from https://developer.getjobber.com/
|
|
3
|
+
JOBBER_CLIENT_ID=
|
|
4
|
+
JOBBER_CLIENT_SECRET=
|
|
5
|
+
|
|
6
|
+
# OAuth2 Tokens
|
|
7
|
+
# Obtained after completing the Jobber OAuth2 authorization flow
|
|
8
|
+
JOBBER_ACCESS_TOKEN=
|
|
9
|
+
JOBBER_REFRESH_TOKEN=
|
|
10
|
+
|
|
11
|
+
# Transport: "stdio" (default, for Claude Code) or "http" (for remote access)
|
|
12
|
+
# TRANSPORT=stdio
|
|
13
|
+
|
|
14
|
+
# HTTP transport settings (only used when TRANSPORT=http)
|
|
15
|
+
# HTTP_PORT=3000
|
|
16
|
+
# HTTP_HOST=localhost
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Onsite-Intelligence
|
|
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
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# @onsite-intelligence/jobber-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for the [Jobber](https://getjobber.com) field service management API — client management, job scheduling, quoting, invoicing, and service request intake. Built for AI agents working in home services.
|
|
4
|
+
|
|
5
|
+
## What is this?
|
|
6
|
+
|
|
7
|
+
This is a "driver" that lets any MCP-compatible AI assistant (Claude, etc.) read and write data in a Jobber account. [MCP (Model Context Protocol)](https://modelcontextprotocol.io) is an open standard that gives AI models a structured way to call external tools — think of it like a USB port between an AI and your business software.
|
|
8
|
+
|
|
9
|
+
With this server running, an AI agent can search for clients, create jobs, check the schedule, send quotes, and more — all through natural conversation.
|
|
10
|
+
|
|
11
|
+
## Available Tools
|
|
12
|
+
|
|
13
|
+
### Clients
|
|
14
|
+
| Tool | Description |
|
|
15
|
+
|------|-------------|
|
|
16
|
+
| `jobber_search_clients` | Search clients by name, email, or phone |
|
|
17
|
+
| `jobber_get_client` | Get full client details, properties, and recent jobs |
|
|
18
|
+
| `jobber_create_client` | Create a new client |
|
|
19
|
+
| `jobber_update_client` | Update an existing client's info |
|
|
20
|
+
|
|
21
|
+
### Requests
|
|
22
|
+
| Tool | Description |
|
|
23
|
+
|------|-------------|
|
|
24
|
+
| `jobber_create_request` | Create a new service request with optional notes |
|
|
25
|
+
| `jobber_list_requests` | List service requests with pagination |
|
|
26
|
+
| `jobber_get_request` | Get full details for a service request |
|
|
27
|
+
|
|
28
|
+
### Jobs
|
|
29
|
+
| Tool | Description |
|
|
30
|
+
|------|-------------|
|
|
31
|
+
| `jobber_list_jobs` | List jobs with status/date filters |
|
|
32
|
+
| `jobber_get_job` | Get job details including line items and visits |
|
|
33
|
+
| `jobber_create_job` | Create a new job with line items |
|
|
34
|
+
| `jobber_update_job_status` | Add a note to a job |
|
|
35
|
+
|
|
36
|
+
### Scheduling
|
|
37
|
+
| Tool | Description |
|
|
38
|
+
|------|-------------|
|
|
39
|
+
| `jobber_get_schedule` | Get schedule for a date range |
|
|
40
|
+
| `jobber_create_visit` | Schedule a visit for an existing job |
|
|
41
|
+
| `jobber_get_availability` | Check available time slots for team members |
|
|
42
|
+
|
|
43
|
+
### Quotes
|
|
44
|
+
| Tool | Description |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `jobber_create_quote` | Create a draft quote with line items |
|
|
47
|
+
| `jobber_list_quotes` | List quotes with pagination |
|
|
48
|
+
|
|
49
|
+
### Invoices
|
|
50
|
+
| Tool | Description |
|
|
51
|
+
|------|-------------|
|
|
52
|
+
| `jobber_list_invoices` | List invoices with status and amounts |
|
|
53
|
+
| `jobber_get_invoice` | Get full invoice details with line items |
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### Prerequisites
|
|
58
|
+
|
|
59
|
+
- Node.js 18+
|
|
60
|
+
- A [Jobber developer app](https://developer.getjobber.com/)
|
|
61
|
+
- OAuth2 credentials (client ID, client secret, access token, refresh token)
|
|
62
|
+
|
|
63
|
+
### Install from npm
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install -g @onsite-intelligence/jobber-mcp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Or clone and build
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/Onsite-Intelligence/Jobber-mcp.git
|
|
73
|
+
cd Jobber-mcp
|
|
74
|
+
npm install
|
|
75
|
+
npm run build
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Configure
|
|
79
|
+
|
|
80
|
+
Copy the example environment file and fill in your Jobber credentials:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cp .env.example .env
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Your `.env` should look like this:
|
|
87
|
+
|
|
88
|
+
```env
|
|
89
|
+
JOBBER_CLIENT_ID=your_client_id
|
|
90
|
+
JOBBER_CLIENT_SECRET=your_client_secret
|
|
91
|
+
JOBBER_ACCESS_TOKEN=your_access_token
|
|
92
|
+
JOBBER_REFRESH_TOKEN=your_refresh_token
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Connect to Claude Code
|
|
96
|
+
|
|
97
|
+
Add to your project's `.mcp.json`:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"jobber": {
|
|
103
|
+
"command": "jobber-mcp",
|
|
104
|
+
"env": {
|
|
105
|
+
"JOBBER_CLIENT_ID": "your_client_id",
|
|
106
|
+
"JOBBER_CLIENT_SECRET": "your_client_secret",
|
|
107
|
+
"JOBBER_ACCESS_TOKEN": "your_access_token",
|
|
108
|
+
"JOBBER_REFRESH_TOKEN": "your_refresh_token"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Or if running from source:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"jobber": {
|
|
121
|
+
"command": "node",
|
|
122
|
+
"args": ["dist/index.js"],
|
|
123
|
+
"cwd": "/path/to/jobber-mcp",
|
|
124
|
+
"env": {
|
|
125
|
+
"JOBBER_CLIENT_ID": "your_client_id",
|
|
126
|
+
"JOBBER_CLIENT_SECRET": "your_client_secret",
|
|
127
|
+
"JOBBER_ACCESS_TOKEN": "your_access_token",
|
|
128
|
+
"JOBBER_REFRESH_TOKEN": "your_refresh_token"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Connect via HTTP (for remote/hosted use)
|
|
136
|
+
|
|
137
|
+
Start the server in HTTP mode:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
TRANSPORT=http HTTP_PORT=3000 jobber-mcp
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The MCP endpoint will be available at `http://localhost:3000/mcp`.
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
| Variable | Required | Default | Description |
|
|
148
|
+
|----------|----------|---------|-------------|
|
|
149
|
+
| `JOBBER_CLIENT_ID` | Yes | — | OAuth2 client ID from your Jobber developer app |
|
|
150
|
+
| `JOBBER_CLIENT_SECRET` | Yes | — | OAuth2 client secret |
|
|
151
|
+
| `JOBBER_ACCESS_TOKEN` | Yes | — | OAuth2 access token (obtained after authorization) |
|
|
152
|
+
| `JOBBER_REFRESH_TOKEN` | Yes | — | OAuth2 refresh token |
|
|
153
|
+
| `TRANSPORT` | No | `stdio` | Transport mode: `stdio` or `http` |
|
|
154
|
+
| `HTTP_PORT` | No | `3000` | Port for HTTP transport |
|
|
155
|
+
| `HTTP_HOST` | No | `localhost` | Host for HTTP transport |
|
|
156
|
+
|
|
157
|
+
## Token Management
|
|
158
|
+
|
|
159
|
+
Jobber access tokens expire after ~1 hour. This server handles token refresh automatically:
|
|
160
|
+
|
|
161
|
+
1. Before each API call, the server checks if the token is expiring within 5 minutes
|
|
162
|
+
2. On a 401 response, the server refreshes the token and retries
|
|
163
|
+
3. After a successful refresh, the new tokens are **written back to your `.env` file** automatically
|
|
164
|
+
4. Tokens are also logged to stderr as a fallback
|
|
165
|
+
|
|
166
|
+
If the `.env` file doesn't exist or isn't writable, the server falls back to stderr-only logging with a warning.
|
|
167
|
+
|
|
168
|
+
The `scripts/extract-tokens.py` utility can pull tokens from an existing Jobber integration if you need to bootstrap credentials.
|
|
169
|
+
|
|
170
|
+
## Rate Limiting
|
|
171
|
+
|
|
172
|
+
Jobber's API allows 60 requests per minute. The server tracks request timestamps and automatically delays when approaching the limit. If a 429 response is received, it retries with exponential backoff (up to 3 attempts).
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jobber OAuth2 token management.
|
|
3
|
+
*
|
|
4
|
+
* Handles access token storage, automatic refresh on 401,
|
|
5
|
+
* and persistence of refreshed tokens back to the .env file.
|
|
6
|
+
*/
|
|
7
|
+
export interface TokenSet {
|
|
8
|
+
accessToken: string;
|
|
9
|
+
refreshToken: string;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadTokensFromEnv(): TokenSet;
|
|
13
|
+
export declare function getTokens(): TokenSet;
|
|
14
|
+
export declare function refreshAccessToken(): Promise<TokenSet>;
|
|
15
|
+
/**
|
|
16
|
+
* Returns true if the access token is expired or will expire within 5 minutes.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isTokenExpiringSoon(): boolean;
|
|
19
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,wBAAgB,iBAAiB,IAAI,QAAQ,CAkB5C;AAED,wBAAgB,SAAS,IAAI,QAAQ,CAKpC;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,QAAQ,CAAC,CAmD5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAG7C"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jobber OAuth2 token management.
|
|
3
|
+
*
|
|
4
|
+
* Handles access token storage, automatic refresh on 401,
|
|
5
|
+
* and persistence of refreshed tokens back to the .env file.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { resolve, dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
let currentTokens = null;
|
|
11
|
+
const JOBBER_TOKEN_URL = "https://api.getjobber.com/api/oauth/token";
|
|
12
|
+
// Resolve .env path relative to project root (works from src/ and dist/)
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const ENV_PATH = resolve(__dirname, "..", "..", ".env");
|
|
15
|
+
export function loadTokensFromEnv() {
|
|
16
|
+
const accessToken = process.env.JOBBER_ACCESS_TOKEN;
|
|
17
|
+
const refreshToken = process.env.JOBBER_REFRESH_TOKEN;
|
|
18
|
+
if (!accessToken || !refreshToken) {
|
|
19
|
+
throw new Error("Missing JOBBER_ACCESS_TOKEN or JOBBER_REFRESH_TOKEN environment variables. " +
|
|
20
|
+
"Set these after completing the Jobber OAuth flow.");
|
|
21
|
+
}
|
|
22
|
+
currentTokens = {
|
|
23
|
+
accessToken,
|
|
24
|
+
refreshToken,
|
|
25
|
+
expiresAt: Date.now() + 55 * 60 * 1000, // assume ~55 min remaining on first load
|
|
26
|
+
};
|
|
27
|
+
return currentTokens;
|
|
28
|
+
}
|
|
29
|
+
export function getTokens() {
|
|
30
|
+
if (!currentTokens) {
|
|
31
|
+
return loadTokensFromEnv();
|
|
32
|
+
}
|
|
33
|
+
return currentTokens;
|
|
34
|
+
}
|
|
35
|
+
export async function refreshAccessToken() {
|
|
36
|
+
const clientId = process.env.JOBBER_CLIENT_ID;
|
|
37
|
+
const clientSecret = process.env.JOBBER_CLIENT_SECRET;
|
|
38
|
+
const tokens = getTokens();
|
|
39
|
+
if (!clientId || !clientSecret) {
|
|
40
|
+
throw new Error("Missing JOBBER_CLIENT_ID or JOBBER_CLIENT_SECRET — cannot refresh token");
|
|
41
|
+
}
|
|
42
|
+
const resp = await fetch(JOBBER_TOKEN_URL, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { "Content-Type": "application/json" },
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
client_id: clientId,
|
|
47
|
+
client_secret: clientSecret,
|
|
48
|
+
grant_type: "refresh_token",
|
|
49
|
+
refresh_token: tokens.refreshToken,
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
if (!resp.ok) {
|
|
53
|
+
const body = await resp.text();
|
|
54
|
+
throw new Error(`Jobber token refresh failed (${resp.status}): ${body}. ` +
|
|
55
|
+
"The user may need to re-authorize the app.");
|
|
56
|
+
}
|
|
57
|
+
const data = (await resp.json());
|
|
58
|
+
currentTokens = {
|
|
59
|
+
accessToken: data.access_token,
|
|
60
|
+
refreshToken: data.refresh_token,
|
|
61
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
62
|
+
};
|
|
63
|
+
// Persist refreshed tokens to .env file
|
|
64
|
+
persistTokensToEnv(data.access_token, data.refresh_token);
|
|
65
|
+
// Also log to stderr as a fallback
|
|
66
|
+
console.error("[jobber-mcp] Token refreshed successfully.");
|
|
67
|
+
console.error(` JOBBER_ACCESS_TOKEN=${data.access_token}`);
|
|
68
|
+
console.error(` JOBBER_REFRESH_TOKEN=${data.refresh_token}`);
|
|
69
|
+
return currentTokens;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns true if the access token is expired or will expire within 5 minutes.
|
|
73
|
+
*/
|
|
74
|
+
export function isTokenExpiringSoon() {
|
|
75
|
+
const tokens = getTokens();
|
|
76
|
+
return Date.now() > tokens.expiresAt - 5 * 60 * 1000;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Write refreshed tokens back to the .env file so they survive restarts.
|
|
80
|
+
* Falls back to stderr-only if the file doesn't exist or isn't writable.
|
|
81
|
+
*/
|
|
82
|
+
function persistTokensToEnv(accessToken, refreshToken) {
|
|
83
|
+
try {
|
|
84
|
+
let content = readFileSync(ENV_PATH, "utf-8");
|
|
85
|
+
content = content.replace(/^JOBBER_ACCESS_TOKEN=.*$/m, `JOBBER_ACCESS_TOKEN=${accessToken}`);
|
|
86
|
+
content = content.replace(/^JOBBER_REFRESH_TOKEN=.*$/m, `JOBBER_REFRESH_TOKEN=${refreshToken}`);
|
|
87
|
+
writeFileSync(ENV_PATH, content, "utf-8");
|
|
88
|
+
console.error("[jobber-mcp] Tokens persisted to .env file.");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
console.error("[jobber-mcp] Warning: could not write tokens to .env file — tokens logged to stderr only.");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jobber GraphQL queries, mutations, and the core request function.
|
|
3
|
+
*
|
|
4
|
+
* All GraphQL operations are defined here as tagged template strings.
|
|
5
|
+
* The jobberRequest() function handles auth, retries, and rate limiting.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Execute a GraphQL request against the Jobber API.
|
|
9
|
+
* Handles token refresh, rate limiting, and retries.
|
|
10
|
+
*/
|
|
11
|
+
export declare function jobberRequest(query: string, variables?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
12
|
+
export declare const SEARCH_CLIENTS = "\nquery SearchClients($searchTerm: String!, $first: Int!, $after: String) {\n clients(searchTerm: $searchTerm, first: $first, after: $after) {\n nodes {\n id\n firstName\n lastName\n name\n isCompany\n companyName\n phones { number description primary }\n emails { address description primary }\n billingAddress { street1 street2 city province postalCode country }\n tags { nodes { label } }\n createdAt\n }\n totalCount\n pageInfo { hasNextPage endCursor }\n }\n}";
|
|
13
|
+
export declare const GET_CLIENT = "\nquery GetClient($id: EncodedId!) {\n client(id: $id) {\n id\n firstName\n lastName\n name\n isCompany\n companyName\n phones { number description primary }\n emails { address description primary }\n billingAddress { street1 street2 city province postalCode country }\n tags { nodes { label } }\n createdAt\n clientProperties(first: 10) {\n nodes {\n id\n address { street1 street2 city province postalCode country }\n }\n }\n jobs(first: 10, orderBy: { key: CREATED_AT, direction: DESC }) {\n nodes {\n id\n title\n jobStatus\n createdAt\n total\n }\n totalCount\n }\n }\n}";
|
|
14
|
+
export declare const CREATE_CLIENT = "\nmutation CreateClient($input: ClientCreateInput!) {\n clientCreate(input: $input) {\n client {\n id\n firstName\n lastName\n name\n jobberWebUri\n }\n userErrors { message path }\n }\n}";
|
|
15
|
+
export declare const UPDATE_CLIENT = "\nmutation UpdateClient($clientId: EncodedId!, $input: ClientUpdateInput!) {\n clientUpdate(clientId: $clientId, input: $input) {\n client {\n id\n firstName\n lastName\n name\n phones { number description primary }\n emails { address description primary }\n }\n userErrors { message path }\n }\n}";
|
|
16
|
+
export declare const CREATE_REQUEST = "\nmutation CreateRequest($input: RequestCreateInput!) {\n requestCreate(input: $input) {\n request {\n id\n title\n requestStatus\n jobberWebUri\n createdAt\n }\n userErrors { message path }\n }\n}";
|
|
17
|
+
export declare const LIST_REQUESTS = "\nquery ListRequests($first: Int!, $after: String) {\n requests(first: $first, after: $after) {\n nodes {\n id\n title\n requestStatus\n createdAt\n client { id name }\n property { address { street1 city province } }\n jobberWebUri\n }\n totalCount\n pageInfo { hasNextPage endCursor }\n }\n}";
|
|
18
|
+
export declare const GET_REQUEST = "\nquery GetRequest($id: EncodedId!) {\n request(id: $id) {\n id\n title\n requestStatus\n createdAt\n client { id name phones { number } emails { address } }\n property { address { street1 street2 city province postalCode } }\n jobberWebUri\n }\n}";
|
|
19
|
+
export declare const CREATE_REQUEST_NOTE = "\nmutation CreateRequestNote($requestId: EncodedId!, $message: String!) {\n requestNoteCreate(requestId: $requestId, message: $message) {\n note { id }\n userErrors { message path }\n }\n}";
|
|
20
|
+
export declare const LIST_JOBS = "\nquery ListJobs($first: Int!, $after: String, $filter: JobFilterAttributes) {\n jobs(first: $first, after: $after, filter: $filter) {\n nodes {\n id\n title\n jobNumber\n jobStatus\n startAt\n endAt\n createdAt\n total\n client { id name }\n property { address { street1 city province postalCode } }\n jobberWebUri\n }\n totalCount\n pageInfo { hasNextPage endCursor }\n }\n}";
|
|
21
|
+
export declare const GET_JOB = "\nquery GetJob($id: EncodedId!) {\n job(id: $id) {\n id\n title\n jobNumber\n jobStatus\n startAt\n endAt\n createdAt\n total\n instructions\n client {\n id\n name\n firstName\n lastName\n phones { number description }\n emails { address description }\n }\n property {\n address { street1 street2 city province postalCode }\n }\n lineItems {\n nodes {\n name\n description\n quantity\n unitPrice\n totalPrice\n }\n }\n visits(first: 20) {\n nodes {\n id\n title\n startAt\n endAt\n status\n assignedUsers { nodes { id name { full } } }\n }\n }\n jobberWebUri\n }\n}";
|
|
22
|
+
export declare const CREATE_JOB = "\nmutation CreateJob($input: JobCreateInput!) {\n jobCreate(input: $input) {\n job {\n id\n title\n jobNumber\n jobStatus\n jobberWebUri\n }\n userErrors { message path }\n }\n}";
|
|
23
|
+
export declare const CREATE_JOB_NOTE = "\nmutation CreateJobNote($jobId: ID!, $message: String!) {\n jobNoteCreate(jobId: $jobId, message: $message) {\n note { id }\n userErrors { message path }\n }\n}";
|
|
24
|
+
export declare const GET_SCHEDULE = "\nquery GetSchedule($startAt: ISO8601Date!, $endAt: ISO8601Date!, $first: Int!, $after: String) {\n jobs(\n filter: {\n startAt: { between: { start: $startAt, end: $endAt } }\n status: [ACTIVE, IN_PROGRESS, TODAY, UPCOMING]\n }\n first: $first\n after: $after\n ) {\n nodes {\n id\n title\n jobStatus\n startAt\n endAt\n client { id name }\n property { address { street1 city province } }\n visits(first: 20) {\n nodes {\n id\n startAt\n endAt\n status\n assignedUsers { nodes { id name { full } } }\n }\n }\n jobberWebUri\n }\n totalCount\n pageInfo { hasNextPage endCursor }\n }\n}";
|
|
25
|
+
export declare const CREATE_VISIT = "\nmutation CreateVisit($input: VisitCreateInput!) {\n visitCreate(input: $input) {\n visit {\n id\n startAt\n endAt\n }\n userErrors { message path }\n }\n}";
|
|
26
|
+
export declare const CREATE_QUOTE = "\nmutation CreateQuote($attributes: QuoteCreateAttributes!) {\n quoteCreate(attributes: $attributes) {\n quote {\n id\n quoteNumber\n quoteStatus\n total\n jobberWebUri\n }\n userErrors { message path }\n }\n}";
|
|
27
|
+
export declare const LIST_QUOTES = "\nquery ListQuotes($first: Int!, $after: String) {\n quotes(first: $first, after: $after) {\n nodes {\n id\n quoteNumber\n quoteStatus\n total\n createdAt\n client { id name }\n jobberWebUri\n }\n totalCount\n pageInfo { hasNextPage endCursor }\n }\n}";
|
|
28
|
+
export declare const LIST_INVOICES = "\nquery ListInvoices($first: Int!, $after: String) {\n invoices(first: $first, after: $after) {\n nodes {\n id\n invoiceNumber\n invoiceStatus\n total\n amountDue\n issuedDate\n dueDate\n createdAt\n client { id name }\n jobberWebUri\n }\n totalCount\n pageInfo { hasNextPage endCursor }\n }\n}";
|
|
29
|
+
export declare const GET_INVOICE = "\nquery GetInvoice($id: EncodedId!) {\n invoice(id: $id) {\n id\n invoiceNumber\n invoiceStatus\n total\n amountDue\n issuedDate\n dueDate\n createdAt\n subject\n message\n client {\n id\n name\n firstName\n lastName\n phones { number }\n emails { address }\n }\n lineItems {\n nodes {\n name\n description\n quantity\n unitPrice\n totalPrice\n }\n }\n jobberWebUri\n }\n}";
|
|
30
|
+
export declare const GET_ACCOUNT = "\nquery GetAccount {\n account {\n id\n name\n phone\n industry\n }\n}";
|
|
31
|
+
export declare const LIST_USERS = "\nquery ListUsers($first: Int!) {\n users(first: $first) {\n nodes {\n id\n name { first last full }\n email { raw }\n role\n }\n }\n}";
|
|
32
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/graphql/queries.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgCH;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA8DlC;AAID,eAAO,MAAM,cAAc,0hBAmBzB,CAAC;AAEH,eAAO,MAAM,UAAU,6rBA+BrB,CAAC;AAEH,eAAO,MAAM,aAAa,oOAYxB,CAAC;AAEH,eAAO,MAAM,aAAa,uVAaxB,CAAC;AAIH,eAAO,MAAM,cAAc,8OAYzB,CAAC;AAEH,eAAO,MAAM,aAAa,0VAexB,CAAC;AAEH,eAAO,MAAM,WAAW,mRAWtB,CAAC;AAEH,eAAO,MAAM,mBAAmB,yMAM9B,CAAC;AAIH,eAAO,MAAM,SAAS,kcAmBpB,CAAC;AAEH,eAAO,MAAM,OAAO,svBA4ClB,CAAC;AAEH,eAAO,MAAM,UAAU,0NAYrB,CAAC;AAEH,eAAO,MAAM,eAAe,8KAM1B,CAAC;AAIH,eAAO,MAAM,YAAY,8tBAgCvB,CAAC;AAEH,eAAO,MAAM,YAAY,2LAUvB,CAAC;AAIH,eAAO,MAAM,YAAY,0PAYvB,CAAC;AAEH,eAAO,MAAM,WAAW,iTAetB,CAAC;AAIH,eAAO,MAAM,aAAa,2WAkBxB,CAAC;AAEH,eAAO,MAAM,WAAW,mfAgCtB,CAAC;AAIH,eAAO,MAAM,WAAW,yFAQtB,CAAC;AAIH,eAAO,MAAM,UAAU,uKAUrB,CAAC"}
|