sgp-sdk 0.0.0-stage → 1.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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -2
  3. package/dist/client.d.ts +228 -0
  4. package/dist/endpoint.d.ts +66 -0
  5. package/dist/endpoints/crm/address.d.ts +20 -0
  6. package/dist/endpoints/crm/create-contract.d.ts +109 -0
  7. package/dist/endpoints/crm/create-customer.d.ts +65 -0
  8. package/dist/endpoints/crm/update-customer-status.d.ts +43 -0
  9. package/dist/endpoints/os/list-occurrence-reasons.d.ts +24 -0
  10. package/dist/endpoints/os/list-occurrence-types.d.ts +25 -0
  11. package/dist/endpoints/precadastro/list-due-days.d.ts +19 -0
  12. package/dist/endpoints/precadastro/list-sellers.d.ts +21 -0
  13. package/dist/endpoints/suporte/add-customer-document.d.ts +24 -0
  14. package/dist/endpoints/suporte/list-customer-documents.d.ts +25 -0
  15. package/dist/endpoints/ura/check-coverage.d.ts +46 -0
  16. package/dist/endpoints/ura/list-annotations.d.ts +32 -0
  17. package/dist/endpoints/ura/list-clients.d.ts +58 -0
  18. package/dist/endpoints/ura/list-contracts.d.ts +45 -0
  19. package/dist/endpoints/ura/list-invoices.d.ts +107 -0
  20. package/dist/endpoints/ura/list-nas.d.ts +29 -0
  21. package/dist/endpoints/ura/list-occurrence-methods.d.ts +19 -0
  22. package/dist/endpoints/ura/list-payment-carriers.d.ts +20 -0
  23. package/dist/endpoints/ura/list-plans.d.ts +70 -0
  24. package/dist/endpoints/ura/list-pops.d.ts +19 -0
  25. package/dist/endpoints/ura/list-service-orders.d.ts +64 -0
  26. package/dist/endpoints/ura/lookup-customer.d.ts +121 -0
  27. package/dist/endpoints/ura/open-ticket.d.ts +81 -0
  28. package/dist/errors.d.ts +59 -0
  29. package/dist/index.d.ts +50 -0
  30. package/dist/index.js +1449 -0
  31. package/dist/result.d.ts +55 -0
  32. package/dist/schema/parse.d.ts +19 -0
  33. package/dist/schema/paths.d.ts +38 -0
  34. package/dist/sensitivity.d.ts +7 -0
  35. package/dist/transport.d.ts +65 -0
  36. package/package.json +34 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SYXS
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,304 @@
1
- # Temporary Holding Version
1
+ # sgp-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
+ Unofficial TypeScript client for the HTTP API of SGP, the ISP ERP by TSMX. Not affiliated with
4
+ TSMX. It is a _dumb_ SDK: typed inputs and outputs, normalized errors, no business rules. Your
5
+ application decides what to do with the data.
6
+
7
+ - Reads, plus writes on `crm`, `ura.openTicket` and `suporte.addCustomerDocument` (see
8
+ [Writes](#writes)).
9
+ - ESM package with types, Node >= 22 (also runs on Bun). The only runtime dependency is `zod`.
10
+ - Methods never throw for network, HTTP or schema failures; they return a result object.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ bun add sgp-sdk
16
+ # or
17
+ npm install sgp-sdk
18
+ ```
19
+
20
+ Types resolve under `"moduleResolution": "bundler"` (Bun, Next.js, tsx, Vite, esbuild) and under
21
+ `node16` / `nodenext`: the emitted `.d.ts` files carry explicit `.js` extensions.
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import { clientPersonalKeys, createSgpClient, omitSensitive } from "sgp-sdk";
27
+
28
+ const sgp = createSgpClient({
29
+ baseUrl: "https://sgp.example.com",
30
+ token: "<token of your app>",
31
+ app: "<app name>",
32
+ // timeoutMs: 20_000, // default
33
+ // fetch: fakeFetch, // default: the global fetch (inject a fake in tests)
34
+ });
35
+
36
+ const plans = await sgp.ura.listPlans();
37
+ if (plans.success) {
38
+ console.log(plans.data.planos.length, plans.warnings ?? []);
39
+ } else {
40
+ console.error(plans.error.code, plans.error.status, plans.error.endpoint);
41
+ }
42
+
43
+ const clients = await sgp.ura.listClients({ cpfcnpj: "00000000000", limit: 1 });
44
+ if (clients.success) {
45
+ for (const client of clients.data.clientes) {
46
+ const safe = omitSensitive(client, clientPersonalKeys); // no name, CPF/CNPJ, address, contacts
47
+ console.log(safe.id);
48
+ }
49
+ }
50
+ ```
51
+
52
+ The SDK never reads the environment: load the credentials your way and pass them in.
53
+
54
+ ### Methods
55
+
56
+ Field names in params and responses are exactly what SGP sends. Each method's JSDoc cites the HTTP
57
+ method and path.
58
+
59
+ | Method | SGP route |
60
+ | ------------------------------------------------------- | ------------------------------------------------------- |
61
+ | `ura.listClients(params?)` | `POST /api/ura/clientes/` |
62
+ | `ura.lookupCustomer(params)` | `POST /api/ura/consultacliente/` |
63
+ | `ura.listContracts(params?)` | `POST /api/ura/listacontrato/` |
64
+ | `ura.listAnnotations(params)` **experimental** | `GET /api/ura/cliente/anotacao/list/` |
65
+ | `ura.listServiceOrders(params?)` | `POST /api/ura/ordemservico/list/` |
66
+ | `ura.listInvoices(params?)` | `POST /api/ura/titulos/` |
67
+ | `ura.listPops()` | `POST /api/ura/pops/` |
68
+ | `ura.listNas()` | `POST /api/ura/nas/list/` |
69
+ | `ura.listPaymentCarriers()` | `GET /api/ura/portador/` |
70
+ | `ura.checkCoverage(params)` | `GET /api/ura/viabilidadeinstalacao` |
71
+ | `ura.listPlans()` | `GET /api/ura/consultaplano/` |
72
+ | `ura.listOccurrenceMethods()` | `GET /api/ura/ocorrencia/metodo/list/` |
73
+ | `ura.openTicket(params)` | `POST /api/ura/chamado/` |
74
+ | `os.listOccurrenceTypes()` | `GET /api/os/ocorrencia/tipo/list/` |
75
+ | `os.listOccurrenceReasons()` | `GET /api/os/ocorrencia/motivo/list/` |
76
+ | `precadastro.listDueDays()` | `POST /api/precadastro/vencimento/list` |
77
+ | `precadastro.listSellers()` | `POST /api/precadastro/vendedor/list` |
78
+ | `crm.createIndividualCustomer(params)` | `POST /api/crm/cliente/F` |
79
+ | `crm.createCompanyCustomer(params)` | `POST /api/crm/cliente/J` |
80
+ | `crm.createContract(clienteId, params)` | `POST /api/crm/cliente/{cliente_id}/contratos/` |
81
+ | `crm.updateCustomerStatus(clienteId, params)` | `POST /api/crm/cliente/{cliente_id}/status/` |
82
+ | `suporte.addCustomerDocument(clienteId, file, params?)` | `PUT /api/suporte/cliente/{cliente_id}/documento/add/` |
83
+ | `suporte.listCustomerDocuments(clienteId)` | `GET /api/suporte/cliente/{cliente_id}/documento/list/` |
84
+
85
+ Every route must be allowed on the consumer's token in SGP (System > Tools > Admin Panel >
86
+ Tokens). A route outside the whitelist returns `ROUTE_NOT_ALLOWED`. Paging is the API's own:
87
+ `offset` and `limit`; the SDK never pages for you.
88
+
89
+ `ura.checkCoverage({ gateway, raio, coordenada })` checks installation viability: the answer is keyed
90
+ by the gateway's name (data-defined, e.g. `SGPMaps`) and holds the CTOs within `raio` meters or the
91
+ message `"Localização inviável"`. The coordinate (`"lat,lng"`) comes from the consumer; the SDK does
92
+ not geocode. `lat`, `lng` and `distancia` are always numbers (Geogrid sends them as numeric strings
93
+ and the SDK converts them). A malformed gateway answer, such as a CTO with a non-numeric `lat`, is
94
+ `SCHEMA_MISMATCH`.
95
+
96
+ ### Experimental annotation listing
97
+
98
+ ```ts
99
+ const notes = await sgp.ura.listAnnotations({ cliente_id: 10, contrato_id: 100 });
100
+ ```
101
+
102
+ `ura.listAnnotations` requires at least one of `cliente_id` / `contrato_id`, each a positive safe-integer
103
+ number. Missing filters, nulls, numeric strings and invalid numbers return `VALIDATION` with status 0
104
+ without fetching. Customer-only includes notes with a null contract; contract-only selects that
105
+ contract; both filters mean **AND**, never OR or fallback. No match is a successful empty array.
106
+
107
+ SGP currently ignores these upstream filters (vendor defect reported, not claimed fixed). The SDK
108
+ still sends canonical filters in one authenticated GET and selects raw identities **before** parsing
109
+ selected notes. A definite mismatch excludes a row even if unrelated content is malformed. If a
110
+ needed identity is missing or malformed and no definite mismatch excludes it, the call fails with
111
+ `SCHEMA_MISMATCH`, not partial data. Source identity strings are not coerced. Selected core fields
112
+ are strict; optional-field drift produces ordinary, selected-only schema warnings. Source order and
113
+ duplicates are preserved; unknown fields are stripped.
114
+
115
+ Returns matching notes present in one SGP response; source completeness is not guaranteed while this method is experimental.
116
+ No documented pagination contract proves completeness; there is no automatic paging, retry, polling,
117
+ cache, completeness flag or operational warning. The method, snake_case params and array-shaped
118
+ `SgpResult<ListAnnotationsResponse>` remain usable after the vendor fix; local selection and
119
+ validation remain as defense. Removing the experimental label requires evidence of corrected behavior.
120
+
121
+ The nine declared fields are required numeric `id` / `cliente`, required number-or-null
122
+ `clientecontrato`, required string `anotacao`, optional/nullable numeric `usuario` / `tipo` /
123
+ `alerta_modo`, and optional/nullable string `data_cadastro` / `data_alteracao`. Text and dates are
124
+ preserved verbatim, without HTML rendering, trimming or date parsing. `annotationPersonalKeys`
125
+ classifies note text and staff identity (`anotacao`, `usuario`); `annotationSecretKeys` is empty.
126
+ Use `omitSensitive(note, annotationPersonalKeys)` before logging or forwarding: free text may contain
127
+ user-entered sensitive content even though no credential field is declared.
128
+
129
+ **Trusted server only:** the multi-customer upstream body enters SDK memory, but the SDK never
130
+ returns, logs or caches the bulk array. Injected fetch, tracing and other instrumentation see the
131
+ body **before** SDK selection and MUST NOT capture it. Local selection is not an authorization
132
+ boundary: the application must authorize requested IDs and keep credentials and execution trusted.
133
+ SDK-generated diagnostics contain paths/types, not source values. Existing upstream error details
134
+ are unchanged and may echo request values; treat them as sensitive too. This special filtered read
135
+ is deliberately excluded from the generic filterless probe registry.
136
+
137
+ ## Result and error contract
138
+
139
+ Every method returns `SgpResult<T>`:
140
+
141
+ ```ts
142
+ type SgpResult<T> =
143
+ | { success: true; data: T; warnings?: SchemaWarning[] }
144
+ | { success: false; error: SgpError };
145
+
146
+ interface SgpError {
147
+ status: number; // HTTP status, 0 when no response arrived
148
+ code: SgpErrorCode;
149
+ message: string; // written by the SDK, in English
150
+ detail?: string; // SGP's own error text (`detail`, `msg`, `errors` or `error`), copied verbatim
151
+ endpoint: string; // path only, never the query string
152
+ }
153
+
154
+ interface SchemaWarning {
155
+ path: string; // e.g. "contratos[0].cobIsento"
156
+ expected: string; // type names only, never values
157
+ received: string;
158
+ }
159
+ ```
160
+
161
+ | Code | When |
162
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
163
+ | `UNAUTHORIZED` | 401/403 with `detail` (or `msg`) `"Credenciais de autenticação incorretas."` (wrong token or app) |
164
+ | `ROUTE_NOT_ALLOWED` | 401/403 with `detail` (or `msg`) `"As credenciais de autenticação não foram fornecidas."`. The SDK always sends credentials, so this means the route is not on the token's whitelist |
165
+ | `NOT_FOUND` | 404 |
166
+ | `METHOD_NOT_ALLOWED` | 405 |
167
+ | `VALIDATION` | any other 4xx: SGP refused the input |
168
+ | `SCHEMA_MISMATCH` | 2xx whose body is not JSON, or whose key fields are missing or have the wrong type (`detail` then lists the failing paths) |
169
+ | `UPSTREAM` | 5xx |
170
+ | `TIMEOUT` | the request exceeded `timeoutMs` |
171
+ | `NETWORK` | `fetch` failed before a response (DNS, connection, TLS) |
172
+ | `UNKNOWN` | 401/403 with any other `detail` or `msg`, or a status the rows above do not cover |
173
+
174
+ SGP answers errors in four formats: `{"detail": "..."}`, `{"msg": "..."}`, `{"errors": ...}` (an
175
+ array of messages or an object of field messages, on CRM routes) and `{"error": "..."}` (e.g. a 404
176
+ `Cadastro não encontrado` on CRM routes). `error.detail` holds, in this order: a string `detail`
177
+ (even empty), a non-empty `msg`, the text of `errors`, a non-empty `error`.
178
+
179
+ Guarantees:
180
+
181
+ - The SDK never puts the token, the app, a URL with query string, a raw response body or the
182
+ original message of a `fetch` exception in anything it returns (`data`, `warnings`, `error`).
183
+ - `error.detail` is SGP's text copied verbatim: it may echo values from your request (for example
184
+ a contract ID or a CPF in a `msg`). The SDK adds nothing to it, so treat it like the request
185
+ itself before logging or showing it.
186
+ - 401/403 are classified by that exact `detail` string, not by status (SGP uses either status for
187
+ the same message). If SGP rewords those messages the code falls to `UNKNOWN`.
188
+ - No retry, no cache, no logging, no automatic paging.
189
+ - Redirects are never followed, so the token and payload are not re-sent to another host: a 3xx
190
+ answer is an `UNKNOWN` error carrying its status (`outcome: "unknown"` on a write).
191
+ - Unknown keys are dropped at every level (`strip`): everything that reaches you was declared and
192
+ classified. Required key fields that fail validation are `SCHEMA_MISMATCH`; any other field with
193
+ an unexpected type becomes `undefined` and is reported in `warnings`.
194
+
195
+ ## Writes
196
+
197
+ Write methods (`crm.*`, `ura.openTicket` and `suporte.addCustomerDocument`) return
198
+ `SgpWriteResult<T>`: the same as `SgpResult<T>`, but `error` also carries an `outcome` that says
199
+ whether SGP applied the write.
200
+
201
+ - `"not_applied"`: SGP refused the request (4xx) or the SDK rejected it before sending it. SGP did
202
+ not apply the write; fix the input and call again.
203
+ - `"unknown"`: no answer (`TIMEOUT`, `NETWORK`), a 5xx, a 3xx (`UNKNOWN`: redirects are never
204
+ followed), or a 2xx whose body does not confirm the write (`VALIDATION` with the real HTTP
205
+ status). The write may have happened: check the state in SGP before repeating it.
206
+ - A 4xx whose body has `errors` as an array (e.g. a 400 with `{"errors": ["Ocorreu um erro: ..."]}`)
207
+ is SGP's generic exception wrapper: it can fail after applying part of the write, so it is
208
+ `"unknown"`, even when `detail` or `msg` come with it. The `code` stays `VALIDATION` and the
209
+ `message` reads "SGP reported an error while processing the write; it may have been applied."
210
+ 401/403/404/405/422 stay `"not_applied"` whatever the body, and so does a 4xx whose `errors` is an
211
+ object of field messages or that carries only `detail` or `msg`.
212
+
213
+ After `outcome: "unknown"`, read the state back before repeating the write:
214
+
215
+ | Write | Read that verifies it |
216
+ | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
217
+ | `crm.createIndividualCustomer`, `crm.createCompanyCustomer` | `ura.listClients({ cpfcnpj })` |
218
+ | `crm.createContract` | `ura.lookupCustomer` or `ura.listContracts` |
219
+ | `ura.openTicket` | `ura.listServiceOrders({ contrato })`; a ticket with `desativa_os: 1` opens no service order, so there is nothing to look up |
220
+ | `suporte.addCustomerDocument` | `suporte.listCustomerDocuments` |
221
+ | `crm.updateCustomerStatus` | run it again; for status 6 on an existing CRM customer, a 404 `NOT_FOUND` means it was applied (it leaves the CRM) |
222
+
223
+ A write only succeeds on a 2xx whose body carries the key fields (for example `cliente_id`). The one
224
+ exception is `suporte.addCustomerDocument`: SGP answers an empty 2xx, which succeeds with
225
+ `data: null`. Success there does not prove the file was stored; verify it with
226
+ `suporte.listCustomerDocuments`. The SDK never retries a write.
227
+
228
+ Sales flow: create the customer (`crm.createIndividualCustomer` or `crm.createCompanyCustomer`),
229
+ create the contract on it (`crm.createContract(clienteId, params)`), then call
230
+ `crm.updateCustomerStatus(clienteId, { status_id: crmStatus.contratoRealizadoNoSgp })` (status 6).
231
+ Without that last step SGP's screen keeps the customer as a CRM customer and blocks contract status
232
+ changes.
233
+
234
+ `crm.createContract` sends the installation address nested (`endereco_instalacao`) and also as the
235
+ loose fields (`logradouro`, `numero`, ...), and the billing address only nested
236
+ (`endereco_cobranca`). The loose fields carry the installation address; a billing address that
237
+ differs from the installation one has not been verified live, so check it on SGP before sending
238
+ divergent addresses.
239
+
240
+ Scheduling an installation goes through `ura.openTicket` with `data_hora_agendamento`
241
+ (`"AAAA-MM-DD HH:MM"`); `os_instalacao` in `crm.createContract` opens the order without a date.
242
+
243
+ The token whitelist accepts regular expressions and the trailing slash counts: release each path
244
+ exactly as in the table above, so `/api/crm/cliente/F` has no final `/` and
245
+ `/api/crm/cliente/{cliente_id}/status/` has one (the `{cliente_id}` routes need a pattern that
246
+ matches any ID).
247
+
248
+ ## Sensitive fields
249
+
250
+ SGP responses carry credentials and personal data. The SDK transcribes them (the consumer decides
251
+ what to show) and marks them: each endpoint exports `as const` tuples with the keys of one response
252
+ item.
253
+
254
+ - `secret`: Central password, PPPoE login and password, Wi-Fi passwords and SSIDs, MACs, links with
255
+ an embedded token.
256
+ - `personal`: CPF/CNPJ, phone, e-mail, address, birth date, customer name, free text typed by
257
+ operators.
258
+
259
+ Tuple names follow the item: `clientSecretKeys` / `clientPersonalKeys`,
260
+ `customerContractSecretKeys` / `customerContractPersonalKeys`, and so on (see the package exports).
261
+ `omitSensitive(item, keys)` returns a shallow copy without those keys and removes them from the
262
+ type, so a leak becomes a compile error:
263
+
264
+ ```ts
265
+ const safe = omitSensitive(client, clientPersonalKeys);
266
+ safe.nome; // type error: `nome` was omitted
267
+ ```
268
+
269
+ The SDK only marks and offers the helper; deciding when to apply it is the consumer's job. Strip
270
+ before logging, caching or forwarding an item to a model.
271
+
272
+ ## Known limitations
273
+
274
+ - **Placeholder arrays.** These fields are declared as arrays of empty objects
275
+ (`z.array(z.object({}))`) because no sample response had content for them. Every element arrives
276
+ as `{}`, with no warning, and what SGP sends inside is dropped:
277
+ - `listClients`: `clientes[].contratos` and `clientes[].titulos` (use `lookupCustomer` and the
278
+ contract methods for those details);
279
+ - `lookupCustomer`: `contratos[].telefones_cargos`, `contratos[].outros` and
280
+ `contratos[].anotacoes`;
281
+ - `listServiceOrders`: `ordens_servicos[].tecnicos_auxiliares` and
282
+ `ordens_servicos[].classificacoes`;
283
+ - `listPlans`: `planos[].combo`;
284
+ - `os.listOccurrenceTypes` and `os.listOccurrenceReasons`: `classificacoes` and `setores` of
285
+ each item;
286
+ - `listInvoices`: `acordados` and `cobrados` of `titulos[].acordoPagamento`.
287
+ - **Guessed types.** SGP only sent `null` for four `lookupCustomer` fields, so their types are
288
+ inferred: `contratos[].contrato_unificado_principal` (number),
289
+ `contratos[].contrato_unificado_filhos` (array of numbers), `contratos[].endereco_pontoreferencia`
290
+ (string) and `contratos[].tags[].categoria_id` (number). A wrong guess does not fail the call: the
291
+ field comes out as `undefined` and is reported in `warnings`.
292
+ - **A bad element drops the whole array.** When one element of an array field has the wrong type,
293
+ the whole field becomes `undefined`, and the warning names only the failing element. For example,
294
+ a non-string entry in `clientes[].contatos.telefones` drops the whole `telefones` array and the
295
+ warning path is `clientes[0].contatos.telefones[1]`.
296
+ - `listContracts` does not transcribe the address fields returned by `exibir_endereco`.
297
+
298
+ ## Pending
299
+
300
+ What is left:
301
+
302
+ - **E-signature of the adhesion term.** Not available in the SGP API.
303
+ - **Boleto delivery methods and invoice (nota fiscal) fields** of the contract. Not available in the
304
+ SGP API, so `crm.createContract` leaves them empty.
@@ -0,0 +1,228 @@
1
+ import { type CreateContractParams, type CreateContractResponse } from "./endpoints/crm/create-contract.js";
2
+ import { type CreateCompanyCustomerParams, type CreateCustomerResponse, type CreateIndividualCustomerParams } from "./endpoints/crm/create-customer.js";
3
+ import { type UpdateCustomerStatusParams, type UpdateCustomerStatusResponse } from "./endpoints/crm/update-customer-status.js";
4
+ import { type ListOccurrenceReasonsResponse } from "./endpoints/os/list-occurrence-reasons.js";
5
+ import { type ListOccurrenceTypesResponse } from "./endpoints/os/list-occurrence-types.js";
6
+ import { type ListDueDaysResponse } from "./endpoints/precadastro/list-due-days.js";
7
+ import { type ListSellersResponse } from "./endpoints/precadastro/list-sellers.js";
8
+ import { type AddCustomerDocumentParams, type AddCustomerDocumentResponse } from "./endpoints/suporte/add-customer-document.js";
9
+ import { type ListCustomerDocumentsResponse } from "./endpoints/suporte/list-customer-documents.js";
10
+ import { type CheckCoverageParams, type CheckCoverageResponse } from "./endpoints/ura/check-coverage.js";
11
+ import { type ListAnnotationsParams, type ListAnnotationsResponse } from "./endpoints/ura/list-annotations.js";
12
+ import { type ListClientsParams, type ListClientsResponse } from "./endpoints/ura/list-clients.js";
13
+ import { type ListContractsParams, type ListContractsResponse } from "./endpoints/ura/list-contracts.js";
14
+ import { type ListInvoicesParams, type ListInvoicesResponse } from "./endpoints/ura/list-invoices.js";
15
+ import { type ListNasResponse } from "./endpoints/ura/list-nas.js";
16
+ import { type ListOccurrenceMethodsResponse } from "./endpoints/ura/list-occurrence-methods.js";
17
+ import { type ListPaymentCarriersResponse } from "./endpoints/ura/list-payment-carriers.js";
18
+ import { type ListPlansResponse } from "./endpoints/ura/list-plans.js";
19
+ import { type ListPopsResponse } from "./endpoints/ura/list-pops.js";
20
+ import { type ListServiceOrdersParams, type ListServiceOrdersResponse } from "./endpoints/ura/list-service-orders.js";
21
+ import { type LookupCustomerParams, type LookupCustomerResponse } from "./endpoints/ura/lookup-customer.js";
22
+ import { type OpenTicketParams, type OpenTicketResponse } from "./endpoints/ura/open-ticket.js";
23
+ import type { SgpResult, SgpWriteResult } from "./result.js";
24
+ import type { SgpClientConfig, SgpFile } from "./transport.js";
25
+ /** Methods that hit the `/api/ura/` routes. All are reads, except `openTicket`: a write that returns an {@link SgpWriteResult}. */
26
+ export interface UraClient {
27
+ /**
28
+ * @experimental Lists annotations: `GET /api/ura/cliente/anotacao/list/`, with local identity selection.
29
+ * Requires positive safe-integer `cliente_id` and/or `contrato_id`; both mean AND.
30
+ * Returns matching notes present in one SGP response; source completeness is not guaranteed while this method is experimental.
31
+ * The bulk upstream body enters SDK memory but is never returned, logged or cached by the SDK.
32
+ * Use only on a trusted server; authorize requested IDs. Injected fetch/tracing must not capture
33
+ * the upstream body. Omit note text/staff identity with `annotationPersonalKeys` before logging.
34
+ * The public API and local validation/selection remain when upstream filtering is fixed.
35
+ */
36
+ listAnnotations: (params: ListAnnotationsParams) => Promise<SgpResult<ListAnnotationsResponse>>;
37
+ /**
38
+ * Lists clients, one page at a time: `POST /api/ura/clientes/`.
39
+ *
40
+ * Filter with `cliente_id`, `cpfcnpj` or `cliente_nome`; page with `offset` and `limit`.
41
+ */
42
+ listClients: (params?: ListClientsParams) => Promise<SgpResult<ListClientsResponse>>;
43
+ /**
44
+ * Looks up a customer and its contracts: `POST /api/ura/consultacliente/`.
45
+ *
46
+ * Requires at least one of `cpfcnpj`, `contrato`, `nome`, `login` or `telefone`. Each contract
47
+ * carries secrets (Central and PPPoE credentials, Wi-Fi, MACs, payoff link) and personal data;
48
+ * strip them with `omitSensitive` and the exported key tuples before logging or forwarding.
49
+ */
50
+ lookupCustomer: (params: LookupCustomerParams) => Promise<SgpResult<LookupCustomerResponse>>;
51
+ /**
52
+ * Lists contract summaries: `POST /api/ura/listacontrato/`.
53
+ *
54
+ * Filter with `contrato`, `plano`, `tipo` or `status`. Without any filter the response may
55
+ * carry the whole contract base. Results contain personal data (name, CPF/CNPJ, e-mail, phone,
56
+ * status reason); strip it with `omitSensitive` and the exported key tuples before logging or
57
+ * forwarding.
58
+ */
59
+ listContracts: (params?: ListContractsParams) => Promise<SgpResult<ListContractsResponse>>;
60
+ /**
61
+ * Lists service orders (OS), one page at a time: `POST /api/ura/ordemservico/list/`.
62
+ *
63
+ * Filter with `ordem_servico`, `status`, `pop`, `motivo` or `contrato`; page with `offset` and
64
+ * `limit`. Results contain personal data (customer name, free-text content); strip it with
65
+ * `omitSensitive` and the exported key tuples before logging or forwarding.
66
+ */
67
+ listServiceOrders: (params?: ListServiceOrdersParams) => Promise<SgpResult<ListServiceOrdersResponse>>;
68
+ /** Lists the plan catalog: `GET /api/ura/consultaplano/`. Takes no parameters. */
69
+ listPlans: () => Promise<SgpResult<ListPlansResponse>>;
70
+ /** Lists the occurrence methods: `GET /api/ura/ocorrencia/metodo/list/`. Takes no parameters. */
71
+ listOccurrenceMethods: () => Promise<SgpResult<ListOccurrenceMethodsResponse>>;
72
+ /**
73
+ * Lists invoices (titulos), one page at a time: `POST /api/ura/titulos/`.
74
+ *
75
+ * Filter with `contrato`, `cliente_id`, `cpfcnpj`, `titulo_id`, `status`, `portador` or the date
76
+ * ranges; order with `ordenar` and `ordenar_ordem`; page with `offset` and `limit`. Results
77
+ * contain personal data (customer name, CPF/CNPJ, statement text) and payment links and PIX
78
+ * codes that may resolve to the debtor; strip them with `omitSensitive` and the exported key
79
+ * tuples before logging or forwarding.
80
+ */
81
+ listInvoices: (params?: ListInvoicesParams) => Promise<SgpResult<ListInvoicesResponse>>;
82
+ /** Lists the POPs (points of presence): `POST /api/ura/pops/`. Takes no parameters. */
83
+ listPops: () => Promise<SgpResult<ListPopsResponse>>;
84
+ /**
85
+ * Lists the NAS (network access servers) and the POPs each serves: `POST /api/ura/nas/list/`.
86
+ * Takes no parameters.
87
+ *
88
+ * `endereco_ip` is network infrastructure; strip it with `omitSensitive` and `nasSecretKeys`
89
+ * before logging or forwarding.
90
+ */
91
+ listNas: () => Promise<SgpResult<ListNasResponse>>;
92
+ /** Lists the payment carriers (portadores): `GET /api/ura/portador/`. Takes no parameters. */
93
+ listPaymentCarriers: () => Promise<SgpResult<ListPaymentCarriersResponse>>;
94
+ /**
95
+ * Checks installation viability around a coordinate: `GET /api/ura/viabilidadeinstalacao`.
96
+ *
97
+ * The SDK does not geocode: the consumer supplies `coordenada` (`"lat,lng"`), the map `gateway`
98
+ * ID (from `/api/ura/listgatewaymapa`) and the `raio` in meters. The answer is keyed by the gateway's name and
99
+ * holds the CTOs within the radius, or `"Localização inviável"` when there are none.
100
+ *
101
+ * A malformed gateway answer (an entry that is neither CTOs nor a message) is `SCHEMA_MISMATCH`.
102
+ */
103
+ checkCoverage: (params: CheckCoverageParams) => Promise<SgpResult<CheckCoverageResponse>>;
104
+ /**
105
+ * Opens a ticket on a contract: `POST /api/ura/chamado/`. Opens an occurrence and, unless
106
+ * `desativa_os: 1`, a service order; `data_hora_agendamento` (`"AAAA-MM-DD HH:MM"`) schedules it.
107
+ *
108
+ * Requires `contrato`. To schedule an installation, send `motivoos` with your instance's
109
+ * installation reason code (see `os.listOccurrenceReasons`) and `data_hora_agendamento`. Success is `status` 1 with a `protocolo`; the response carries the
110
+ * customer's name and CPF/CNPJ: strip them with `omitSensitive` and `ticketOpenedPersonalKeys`
111
+ * before logging or forwarding. `contato_nome` and `contato_telefone` in the params are personal
112
+ * data.
113
+ *
114
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
115
+ */
116
+ openTicket: (params: OpenTicketParams) => Promise<SgpWriteResult<OpenTicketResponse>>;
117
+ }
118
+ /** Methods that hit the `/api/os/` routes. */
119
+ export interface OsClient {
120
+ /** Lists the occurrence types of service orders (OS): `GET /api/os/ocorrencia/tipo/list/`. Takes no parameters. */
121
+ listOccurrenceTypes: () => Promise<SgpResult<ListOccurrenceTypesResponse>>;
122
+ /** Lists the occurrence reasons of service orders (OS): `GET /api/os/ocorrencia/motivo/list/`. Takes no parameters. */
123
+ listOccurrenceReasons: () => Promise<SgpResult<ListOccurrenceReasonsResponse>>;
124
+ }
125
+ /** Methods that hit the `/api/precadastro/` routes. */
126
+ export interface PrecadastroClient {
127
+ /** Lists the due days offered at pre-registration: `POST /api/precadastro/vencimento/list`. Takes no parameters. */
128
+ listDueDays: () => Promise<SgpResult<ListDueDaysResponse>>;
129
+ /**
130
+ * Lists the sellers (vendedores): `POST /api/precadastro/vendedor/list`. Takes no parameters.
131
+ *
132
+ * Used for `vendedor_id` when creating contracts (v1.1). Sellers are employees: name, CPF and
133
+ * e-mail are personal data; strip them with `omitSensitive` and `sellerPersonalKeys` before
134
+ * logging or forwarding.
135
+ */
136
+ listSellers: () => Promise<SgpResult<ListSellersResponse>>;
137
+ }
138
+ /** Methods that hit the `/api/crm/` routes. Writes: they return an {@link SgpWriteResult}. */
139
+ export interface CrmClient {
140
+ /**
141
+ * Creates an individual (PF) customer: `POST /api/crm/cliente/F`.
142
+ *
143
+ * Requires `nome`, `cpfcnpj` and `endereco`; the address is sent nested and as loose fields.
144
+ * The response `message` carries the name and CPF; strip it with `omitSensitive` and
145
+ * `customerCreatedPersonalKeys` before logging or forwarding.
146
+ *
147
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
148
+ */
149
+ createIndividualCustomer: (params: CreateIndividualCustomerParams) => Promise<SgpWriteResult<CreateCustomerResponse>>;
150
+ /**
151
+ * Creates a company (PJ) customer: `POST /api/crm/cliente/J`.
152
+ *
153
+ * Requires `nome`, `cpfcnpj` and `endereco`; the address is sent nested and as loose fields.
154
+ * The response `message` carries the name and CNPJ; strip it with `omitSensitive` and
155
+ * `customerCreatedPersonalKeys` before logging or forwarding.
156
+ *
157
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
158
+ */
159
+ createCompanyCustomer: (params: CreateCompanyCustomerParams) => Promise<SgpWriteResult<CreateCustomerResponse>>;
160
+ /**
161
+ * Creates a contract on a CRM customer: `POST /api/crm/cliente/{cliente_id}/contratos/`.
162
+ *
163
+ * Requires `pop_id`, `plano_id`, `portador_id`, `forma_cobranca_codigo` (`formaCobrancaCodigo`),
164
+ * `vencimento_dia` and both addresses: `endereco_instalacao` and `endereco_cobranca`, sent
165
+ * nested; the installation one also goes as loose fields. SGP requires `login` and `senha`
166
+ * (PPPoE) for an internet service. The params carry secrets (`login`, `senha`, `central_login`,
167
+ * `central_senha`): never log them. The response `cliente` is the customer's name; strip it with
168
+ * `omitSensitive` and `contractCreatedPersonalKeys` before logging or forwarding.
169
+ *
170
+ * The loose address fields carry the installation address. A billing address different from the
171
+ * installation one has not been verified live. `error.detail` is SGP's text and may echo submitted
172
+ * values (e.g. `senha`): treat it as sensitive before logging.
173
+ *
174
+ * `os_instalacao` opens the installation service order without a date; to schedule one, use
175
+ * `ura.openTicket`. The boleto delivery methods and the invoice (nota fiscal) are not in the
176
+ * API: they stay empty.
177
+ *
178
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
179
+ */
180
+ createContract: (clienteId: number, params: CreateContractParams) => Promise<SgpWriteResult<CreateContractResponse>>;
181
+ /**
182
+ * Sets the CRM status of a customer: `POST /api/crm/cliente/{cliente_id}/status/`.
183
+ *
184
+ * Set `crmStatus.contratoRealizadoNoSgp` after the contract, or SGP's screen keeps the customer
185
+ * as a CRM customer and blocks contract status changes. The response `message` carries the
186
+ * customer's name; strip it with `omitSensitive` and `customerStatusPersonalKeys` before
187
+ * logging or forwarding.
188
+ *
189
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
190
+ */
191
+ updateCustomerStatus: (clienteId: number, params: UpdateCustomerStatusParams) => Promise<SgpWriteResult<UpdateCustomerStatusResponse>>;
192
+ }
193
+ /** Methods that hit the `/api/suporte/` routes. */
194
+ export interface SuporteClient {
195
+ /**
196
+ * Attaches a document to a customer: `PUT /api/suporte/cliente/{cliente_id}/documento/add/`,
197
+ * sent as multipart with the file in the `file` part.
198
+ *
199
+ * SGP answers 200 with an empty body and does not return the document ID: `data` is `null`. To
200
+ * check the upload, use `listCustomerDocuments`. SGP converts images to `.jpeg`, so the stored
201
+ * file name may differ from `file.filename`. `descricao` is personal data when it names the
202
+ * customer.
203
+ *
204
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
205
+ */
206
+ addCustomerDocument: (clienteId: number, file: SgpFile, params?: AddCustomerDocumentParams) => Promise<SgpWriteResult<AddCustomerDocumentResponse>>;
207
+ /**
208
+ * Lists the documents attached to a customer: `GET /api/suporte/cliente/{cliente_id}/documento/list/`.
209
+ *
210
+ * Each document carries the file name or link (`arquivo`), its description and the operator:
211
+ * personal data; strip it with `omitSensitive` and `customerDocumentPersonalKeys` before logging
212
+ * or forwarding.
213
+ */
214
+ listCustomerDocuments: (clienteId: number) => Promise<SgpResult<ListCustomerDocumentsResponse>>;
215
+ }
216
+ /**
217
+ * SGP client. Reads return an {@link SgpResult}, writes (`crm`, `ura.openTicket`,
218
+ * `suporte.addCustomerDocument`) an {@link SgpWriteResult}; no method ever throws.
219
+ */
220
+ export interface SgpClient {
221
+ ura: UraClient;
222
+ os: OsClient;
223
+ precadastro: PrecadastroClient;
224
+ crm: CrmClient;
225
+ suporte: SuporteClient;
226
+ }
227
+ /** Creates an SGP client bound to `config`; `token` and `app` are sent on every request. */
228
+ export declare const createSgpClient: (config: SgpClientConfig) => SgpClient;
@@ -0,0 +1,66 @@
1
+ import type { z } from "zod";
2
+ import type { SgpResult, SgpWriteResult } from "./result.js";
3
+ import { type ParamValue, type RequestSpec, type SgpClientConfig, type SgpFile } from "./transport.js";
4
+ /** Static description of one SGP route: how to call it, how to parse it and what is sensitive. */
5
+ export interface EndpointDefinition<S extends z.ZodType> {
6
+ method: "GET" | "POST";
7
+ /** Request path starting with `/`; also the `endpoint` of every error. */
8
+ path: string;
9
+ schema: S;
10
+ /** Normalized paths of the required, strict fields (see `parseResponse`). */
11
+ keyFields: readonly string[];
12
+ /** Normalized path of the response items that carry sensitive keys, e.g. `clientes[]`. */
13
+ itemPath: string;
14
+ /** Keys of an item holding secrets (passwords, MACs, links with embedded token). */
15
+ secretKeys: readonly string[];
16
+ /** Keys of an item holding personal data (CPF/CNPJ, phone, e-mail, address, name). */
17
+ personalKeys: readonly string[];
18
+ }
19
+ /**
20
+ * Static description of one SGP write route. Like {@link EndpointDefinition}, but `schema` is the
21
+ * success body: a 2xx only counts as success when it validates (key fields present, right type).
22
+ */
23
+ export interface WriteEndpointDefinition<S extends z.ZodType> {
24
+ kind: "write";
25
+ method: "POST" | "PUT";
26
+ /** Request path template, e.g. `/api/crm/cliente/{cliente_id}/contratos/`; also the `endpoint` of every error. */
27
+ path: string;
28
+ schema: S;
29
+ /** Normalized paths of the required, strict fields (see `parseResponse`). */
30
+ keyFields: readonly string[];
31
+ /** Normalized path of the response items that carry sensitive keys. */
32
+ itemPath: string;
33
+ /** Keys of an item holding secrets. */
34
+ secretKeys: readonly string[];
35
+ /** Keys of an item holding personal data. */
36
+ personalKeys: readonly string[];
37
+ }
38
+ /** Inputs of one write call: values for the path placeholders, the request params and any files. */
39
+ export interface WriteCall {
40
+ pathParams?: Record<string, string | number>;
41
+ params?: Record<string, ParamValue | undefined>;
42
+ files?: Record<string, SgpFile>;
43
+ }
44
+ /**
45
+ * Sends the request described by `def` and parses the response with its schema.
46
+ *
47
+ * Never throws: transport and HTTP failures come back as `{ success: false, error }` and a body
48
+ * that does not match the schema as `SCHEMA_MISMATCH`.
49
+ */
50
+ export declare const callEndpoint: <S extends z.ZodType>(config: SgpClientConfig, def: EndpointDefinition<S>, params?: RequestSpec["params"], pathParams?: RequestSpec["pathParams"]) => Promise<SgpResult<z.output<S>>>;
51
+ /**
52
+ * Sends the write described by `def` and tells whether SGP applied it.
53
+ *
54
+ * Never throws. Success is a 2xx whose body validates against `def.schema`. A failure carries an
55
+ * `outcome`:
56
+ * - `"not_applied"`: SGP answered 4xx and rejected the request, or the SDK rejected it locally before
57
+ * sending (invalid path params, `VALIDATION` with status 0);
58
+ * - `"unknown"`: no response (timeout, network), 5xx, 3xx (redirects are never followed), a 4xx
59
+ * whose body `errors` is an array (SGP's generic exception wrapper, except 401/403/404/405/422),
60
+ * or a 2xx that is not a valid success (empty or non-JSON body, key field missing). The 2xx case
61
+ * is a `VALIDATION` error with the real HTTP status and, for a JSON body, SGP's own text
62
+ * (`detail`/`msg`/`errors`/`error`) or else the failing paths.
63
+ *
64
+ * Never retried by the SDK. With `outcome: "unknown"`, check the state in SGP before repeating.
65
+ */
66
+ export declare const callWriteEndpoint: <S extends z.ZodType>(config: SgpClientConfig, def: WriteEndpointDefinition<S>, call?: WriteCall) => Promise<SgpWriteResult<z.output<S>>>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Address as SGP takes it. Pass it once: the SDK serializes it both nested (`endereco`) and as
3
+ * loose fields (`logradouro`, `numero`, ...), which are the two formats the CRM routes read.
4
+ */
5
+ export interface SgpAddress {
6
+ logradouro: string;
7
+ numero?: string | number;
8
+ complemento?: string;
9
+ bairro: string;
10
+ cidade: string;
11
+ cep: string;
12
+ uf: string;
13
+ pais?: string;
14
+ pontoreferencia?: string;
15
+ /** Map position, `"lat,lng"`. */
16
+ map_ll?: string;
17
+ condominio?: string | number;
18
+ }
19
+ /** The address as a plain record with the same keys and no `undefined` ones. */
20
+ export declare const flatAddress: (address: SgpAddress) => Record<string, string | number>;