# @ikatec/digisac-api-sdk

TypeScript SDK for the [Digisac](https://digisac.com.br) API.

## Installation

```bash
npm install @ikatec/digisac-api-sdk
```

## Quick Start

```typescript
import { BaseApiClient } from '@ikatec/digisac-api-sdk'
import { TicketsApi } from '@ikatec/digisac-api-sdk/tickets'
import { PeopleApi } from '@ikatec/digisac-api-sdk/people'

const client = new BaseApiClient('https://api.digisac.com/v1')
client.setAccessToken('your-access-token')

const tickets = new TicketsApi(client)
const people = new PeopleApi(client)

// List with pagination
const result = await tickets.getMany({
  page: 1,
  perPage: 20,
  where: { status: 'open' },
  order: [['createdAt', 'DESC']],
})
console.log(result.data) // Ticket[]
console.log(result.total) // number

// Get by ID
const ticket = await tickets.getById('ticket-uuid')

// Create
const newTicket = await tickets.create({ /* CreateTicketPayload */ })

// Update
const updated = await tickets.updateById('ticket-uuid', { /* UpdateTicketPayload */ })

// Delete
await tickets.deleteById('ticket-uuid')
```

## Architecture

The SDK is structured in two layers:

### Core (`src/core/`)

| File | Description |
|---|---|
| `ApiClient.ts` | Interface defining `setAccessToken` and HTTP methods (`get`, `post`, `put`, `patch`, `delete`, `request`) |
| `BaseApiClient.ts` | `fetch`-based implementation of `ApiClient`. Handles JSON serialization and error parsing into `ApiError`. Automatically injects `Authorization: Bearer` header when an access token is set |
| `BaseCrudApi.ts` | Generic class providing `getMany`, `getById`, `create`, `updateById`, `deleteById` |
| `types.ts` | Shared types: `WhereClause<T>`, `ListQuery<T>`, `Paginated<T>`, `IncludeItem<T>` |

### API Resources (`src/apis/<resource>/`)

Each resource has its own folder with:
- `<Resource>Api.ts` — extends `BaseCrudApi` with the resource path and type parameters
- `types.ts` — TypeScript types for the resource model, create payload, and update payload
- `index.ts` — barrel file re-exporting the API class and all types

Resources are importable directly by name:

```typescript
import { DepartmentsApi, Department } from '@ikatec/digisac-api-sdk/departments'
```

Available resources:

| Resource | Class | Path |
|---|---|---|
| Absence | `AbsenceApi` | `/absence` |
| Acceptance Terms | `AcceptanceTermsApi` | `/acceptance-terms` |
| Accounts | `AccountsApi` | `/accounts` |
| Activity Log | `ActivityLogApi` | `/activity-log` |
| Answers | `AnswersApi` | `/answers` |
| Auth History | `AuthHistoryApi` | `/auth-history` |
| Bot Versions | `BotVersionsApi` | `/bot-versions` |
| Bots | `BotsApi` | `/bots` |
| Campaigns | `CampaignsApi` | `/campaigns` |
| Cards | `CardsApi` | `/cards` |
| Categories | `CategoriesApi` | `/categories` |
| Client Feedback | `ClientFeedbackApi` | `/client-feedback` |
| Contact Block Lists | `ContactBlockListsApi` | `/contact-block-lists` |
| Contact Block Lists Controls | `ContactBlockListsControlsApi` | `/contact-block-lists-controls` |
| Contacts | `ContactsApi` | `/contacts` |
| Credit Movements | `CreditMovementsApi` | `/credit-movements` |
| Custom Fields | `CustomFieldsApi` | `/custom-fields` |
| Departments | `DepartmentsApi` | `/departments` |
| Distribution | `DistributionApi` | `/distribution` |
| Files | `FilesApi` | `/files` |
| Holidays | `HolidayApi` | `/holidays` |
| Integrations | `IntegrationsApi` | `/integrations` |
| Interactive Messages | `InteractiveMessagesApi` | `/interactive-messages` |
| Kanban Boards | `KanbanBoardsApi` | `/kanban-boards` |
| Kanban Cards | `KanbanCardsApi` | `/kanban-cards` |
| Kanban Columns | `KanbanColumnsApi` | `/kanban-columns` |
| Knowledge Base | `KnowledgeBaseApi` | `/knowledge-base` |
| Knowledge Base Items | `KnowledgeBaseItemApi` | `/knowledge-base-items` |
| Me | `MeApi` | `/me` |
| Messages | `MessagesApi` | `/messages` |
| Notifications | `NotificationsApi` | `/notifications` |
| Organizations | `OrganizationsApi` | `/organizations` |
| People | `PeopleApi` | `/people` |
| Permissions | `PermissionsApi` | `/permissions` |
| Personal Access Tokens | `PersonalAccessTokensApi` | `/personal-access-tokens` |
| Pipelines | `PipelineApi` | `/pipelines` |
| Plan AI History | `PlanAiHistoryApi` | `/plan-ai-history` |
| Plans | `PlansApi` | `/plans` |
| Questions | `QuestionsApi` | `/questions` |
| Quick Replies | `QuickRepliesApi` | `/quick-replies` |
| Roles | `RolesApi` | `/roles` |
| Schedules | `ScheduleApi` | `/schedules` |
| Service Access Management | `ServiceAccessManagementApi` | `/service-access-management` |
| Service Events | `ServiceEventsApi` | `/service-events` |
| Services | `ServicesApi` | `/services` |
| Sticker Users | `StickerUsersApi` | `/sticker-users` |
| Stickers | `StickersApi` | `/stickers` |
| Tags | `TagsApi` | `/tags` |
| Terms | `TermsApi` | `/terms` |
| Ticket Topics | `TicketTopicsApi` | `/ticket-topics` |
| Tickets | `TicketsApi` | `/tickets` |
| Timetables | `TimetableApi` | `/timetables` |
| Transcripts | `TranscriptsApi` | `/transcripts` |
| Users | `UsersApi` | `/users` |
| Webhooks | `WebhooksApi` | `/webhooks` |
| WhatsApp Business Templates | `WhatsappBusinessTemplatesApi` | `/whatsapp-business-templates` |

## Querying

`getMany` accepts a `ListQuery<T>` object with Sequelize-style filtering:

```typescript
const result = await people.getMany({
  where: {
    name: { $iLike: '%john%' },
    createdAt: { $gte: '2025-01-01' },
    $or: [
      { email: { $ne: null } },
      { phone: { $ne: null } },
    ],
  },
  include: ['tags'],
  order: [['name', 'ASC']],
  page: 1,
  perPage: 50,
})
```

### Supported operators

| Operator | Description |
|---|---|
| `$eq`, `$ne` | Equal / Not equal |
| `$gt`, `$gte`, `$lt`, `$lte` | Comparisons |
| `$like`, `$iLike` | Pattern matching (case-sensitive / insensitive) |
| `$in`, `$notIn` | Array membership |
| `$between`, `$notBetween` | Range checks |
| `$and`, `$or` | Logical combinators |

## Error Handling

Failed requests throw an `ApiError` with structured information:

```typescript
import { ApiError } from '@ikatec/digisac-api-sdk'

try {
  await tickets.create(payload)
} catch (error) {
  if (error instanceof ApiError) {
    console.log(error.message)          // Error message from API
    console.log(error.status)           // HTTP status code
    console.log(error.errorClass)       // Error class identifier
    console.log(error.validationErrors) // Field-level validation errors
  }
}
```

## Incoming Webhooks

The SDK ships a fully-typed `WebhookPayload<E>` envelope and a `WebhookEventPayloadMap` that narrows `data` to the correct resource type for every supported event.

### Import

```typescript
import type {
  WebhookPayload,
  WebhookEvent,
  WebhookEventPayloadMap,
} from '@ikatec/digisac-api-sdk/incommingWebhooks'
```

### Basic usage

```typescript
import type { WebhookPayload } from '@ikatec/digisac-api-sdk/incommingWebhooks'

// Express / Fastify handler — body is already parsed JSON
app.post('/webhook', (req, res) => {
  const payload = req.body as WebhookPayload

  console.log(payload.event)     // WebhookEvent union
  console.log(payload.webhookId) // string
  console.log(payload.timestamp) // string
  console.log(payload.data)      // narrowed to the right resource type
  res.sendStatus(200)
})
```

### Narrowing by event

Use a generic parameter or a discriminated union to get fully-typed `data`:

```typescript
import type { WebhookPayload } from '@ikatec/digisac-api-sdk/incommingWebhooks'

function handleWebhook(payload: WebhookPayload) {
  if (payload.event === 'message.created') {
    // payload.data is Omit<Message, MessageRelationships>
    console.log(payload.data.text)
  }

  if (payload.event === 'ticket.updated') {
    // payload.data is Omit<Ticket, TicketRelationships>
    console.log(payload.data.isOpen)
  }

  if (payload.event === 'bot.command') {
    // payload.data is BotCommandData
    console.log(payload.data.command)
  }
}
```

You can also parameterise the type when you already know the event:

```typescript
import type { WebhookPayload } from '@ikatec/digisac-api-sdk/incommingWebhooks'

function handleMessageCreated(payload: WebhookPayload<'message.created'>) {
  // payload.data is Omit<Message, MessageRelationships>
  console.log(payload.data.type)      // MessageType
  console.log(payload.data.timestamp) // string
}
```

### Supported events

| Event | `data` type |
|---|---|
| `message.created` / `message.updated` | `Omit<Message, MessageRelationships>` |
| `ticket.created` / `ticket.updated` | `Omit<Ticket, TicketRelationships>` |
| `contact.created` / `contact.updated` / `contact.destroyed` | `Omit<Contact, ContactRelationships>` |
| `service.created` / `service.updated` / `service.destroyed` | `Omit<Service, ServiceRelationships>` |
| `user.created` / `user.updated` / `user.destroyed` | `Omit<User, UserRelationships>` |
| `department.*` | `Omit<Department, DepartmentRelationships>` |
| `people.*` | `Omit<Person, PersonRelationships>` |
| `organization.*` | `Omit<Organization, OrganizationRelationships>` |
| `role.*` | `Omit<Role, RoleRelationships>` |
| `bot.created` / `bot.updated` / `bot.destroyed` / `bot.enter-context` / `bot.leave-context` / `bot.api-signal` | `Omit<Bot, BotRelationships>` |
| `bot.command` | `BotCommandData` |
| `quickReplies.*` | `Omit<QuickReply, QuickReplyRelationships>` |
| `tag.*` | `Omit<Tag, TagRelationships>` |
| `campaign.*` | `Omit<Campaign, CampaignRelationships>` |
| `pipeline.created` / `pipeline.updated` | `Omit<Pipeline, PipelineRelationships>` |
| `ticketTopics.*` | `TicketTopic` |

## Custom API Client

You can provide your own `ApiClient` implementation (e.g. to add auth headers, logging, or use a different HTTP library):

```typescript
import type { ApiClient } from '@ikatec/digisac-api-sdk'

class MyApiClient implements ApiClient {
  // Implement setAccessToken, get, post, put, patch, delete, request
}
```

## Development

```bash
npm run lint          # Run ESLint
npm run lint:fix      # Fix lint issues
npm run format        # Format with Prettier
npm run format:check  # Check formatting
npm run test:types    # Run type-level tests with tsd
```
