Skip to content

[Feature] add Zendesk integration for Tickets module #228

@marcinkrasowski

Description

@marcinkrasowski

Feature: Zendesk integration for retrieving user tickets

Summary

Build a new ZendeskTicketService integration that implements the TicketService contract to retrieve tickets for a user from Zendesk using the Search API. User identification must be done by matching email address. Creating tickets is intentionally out of scope for this task.

Contract reference

The integration must implement the TicketService abstract class:

abstract class TicketService {
    abstract getTicket(
        options: Tickets.Request.GetTicketParams,
        authorization?: string,
    ): Observable<Tickets.Model.Ticket | undefined>;
    abstract getTicketList(
        options: Tickets.Request.GetTicketListQuery,
        authorization?: string,
    ): Observable<Tickets.Model.Tickets>;
    abstract createTicket(
        data: Tickets.Request.PostTicketBody,
        authorization?: string,
    ): Observable<Tickets.Model.Ticket>;
}

Scope

  • Implement ZendeskTicketService conforming to TicketService.
  • Implement getTicketList via Zendesk Support Search API with filtering and pagination.
  • Implement getTicket using the Tickets API primarily (freshest data), with Search API as a fallback, and ensure the ticket belongs to the requesting user (email match).
  • Map responses to our internal Tickets.Model.Ticket and Tickets.Model.Tickets types.

Out of scope

  • createTicket — leave unimplemented with an explicit not-implemented error.

API

getTickets

  • Use Search API that supports ticket filtering and pagination.
  • Base query for a user’s tickets: type:ticket requester:<email>
  • Allow additional filters based on our request options:
    • status
    • priority
    • date ranges
  • Pagination: our public query uses offset and limit (see Tickets model).

getTicket

  • Prefer Request API (probably preferable, as it only returns public information that is available to the end user) or Tickets API for freshness and performance, then validate the requester matches the user’s email if necessary.
  • Retrieve ticket attachments and comments using separate API request, unless there is a way to fetch them together with the ticket details all at once (this would be preferable as it would limit the total number of API requests).

Authentication

  • Support API token auth only. Configure via env vars.
  • Use ZENDESK_API_URL as the full base API URL.
  • Use ZENDESK_API_TOKEN as an already base64-encoded token and send it in the header: Authentication: Base ${ZENDESK_API_TOKEN}.

User identity

  • Identify users by email address. The email must be available via authorization context or request options (document the exact source in the implementation and README). All results must be scoped to that email.
  • To get current user, use the getCurrentUser method from the users module in your ZendeskTicketService:
    constructor(
        private readonly usersService: Users.Service,
    ) {}
    ...
    const user = this.usersService.getCurrentUser();
    
    see user-account module for an example how to use Users.Service.

Error handling and rate limiting

  • Map Zendesk HTTP errors to domain errors like 401/403 (auth), 404 (not found) and others.

Configuration

Environment variables (names may be refined during implementation):

  • ZENDESK_API_URL (full base API URL)
  • ZENDESK_API_TOKEN (base64-encoded, used in Authentication: Base ${ZENDESK_API_TOKEN})

Data mapping

Zendesk responses must be mapped to our normalized Tickets model defined in tickets.model.ts and documented here: Tickets model and service contract.

Required fields in our model (Ticket) to populate:

  • id, createdAt, updatedAt, topic, type, status,
  • properties for other fields like subject, description, address or a form of contact (key/value pairs as needed)

Optional fields:

  • attachments (map filename/url/size/author/date)
  • comments (author/date/content)

Guidance:

  • You might need to fetch ticket fields in order to correctly resolve and map fields.
  • Use Zendesk ticket topictopic; map Zendesk status to our TicketStatus enum; map prioritytype.
  • Preserve ISO timestamps for createdAt/updatedAt.
  • Ensure all returned lists conform to Pagination.Paginated<Ticket> (total, data).
  • See the data model details and examples in the docs: Tickets model.
  • See how mocked tickets look that are displayed at our demo: https://demo.openselfservice.com/en/cases

Acceptance criteria

  • ZendeskTicketService is implemented in a new package (e.g., packages/integrations/zendesk).
  • getTicketList(options, authorization?):
    • Returns only tickets for the email-identified user.
    • Supports filters aligned with our model (see docs): status, type, topic, dateFrom, dateTo, sort (map to Zendesk query as feasible), plus optional priority and tags if present in request.
    • Supports pagination via offset/limit; correctly maps to Zendesk page/per_page and back.
    • Maps data to Tickets.Model.Tickets correctly.
  • getTicket(options, authorization?):
    • Returns the ticket if it belongs to the user; otherwise undefined (or not-found per domain conventions).
    • Uses Tickets API primarily; falls back to Search API when necessary, with email validation.
  • createTicket explicitly throws a not-implemented error referencing this issue.
  • README/docs added for configuration, enabling in DI, and examples.

Implementation notes

  • Location: packages/integrations/zendesk/src/modules/tickets/zendesk-ticket.service.ts.
  • Pagination mapping: input offset/limit ↔ Zendesk page/per_page. Translate next_page/previous_page to new offset (see Tickets model).
  • For fetching data from API:
    1. Prefer an official Zendesk JavaScript SDK/client if available (for stability, auth handling, and API parity).
    2. If no official SDK, and an OpenAPI/Swagger spec is available, generate a typed client and models (e.g., with openapi-generator-cli) and use that.
    3. Otherwise, implement calls using NestJS HTTP module. See NestJS docs: HTTP module.

Follow our integration conventions

  • Creating a new integration package: see Adding new integrations
  • Scaffolding with generators: see Using generators
  • How modules are wired and used in the API Harmonization app: see Data flow
  • Ensure your package exports ./integration and registers the tickets service in Config as described in the guides above.

Align with the Tickets data model

  • Refer to the Tickets model and service contract for supported query parameters and normalized return types. Map status, type, topic, dateFrom, dateTo, and sort to Zendesk Search API where possible, documenting any limitations.

Documentation

  • Add a new docs page under our docs app at apps/docs/docs/integrations/tickets/zendesk.md.
  • Follow the structure used by existing integration descriptions (see examples: Strapi CMS, Algolia, Medusa):
    • Overview and prerequisites
    • Configuration (env vars)
    • Usage examples (getTicket, getTicketList), filters/pagination
    • Data mapping to the normalized Tickets model (link to Tickets model)
    • Limitations and troubleshooting

Test data and environment variables

Switch the app to use the new Zendesk tickets integration following the guide: Switching integrations.

Use the following environment variables to test against our Zendesk instance (provided API token has read-only access to Zendesk):

ZENDESK_API_URL=https://d3v-hycom-20915.zendesk.com
ZENDESK_API_TOKEN=bzJzLWdoQGh5Y29tLnBsL3Rva2VuOjIwU1BZMUFHQnN0SThpd3lhWjQwQTVuUGt1Y09qR0RwZmptVHRhcHk=

In this instance, there are several sample tickets prepared for the user jane@example.com. You can sign in using this user to O2S (see credendials) and go to http://localhost:3000/en/cases to view the tickets retrieved from Zendesk.

As an additional requirement, please provide a short feedback on your experiences with working with this framework - how easy or difficult it was to get started (including starting the project, getting around the monorepo or readings our docs) and to make the required changes.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions