swagger: '2.0'
info:
title: PlanRadar's API Documentation Approval Requests V2 Tickets V2 API
version: '2.0'
description: "Welcome to PlanRadar's API documentation, here you can find all the details about our APIs as well as test them online.<br />\n <h5>Rate Limits</h5>30 requests per minute per account, aggregated across all tokens.<br />\n If the threshold is exceeded, a 5-minute cooldown is applied to the account, aggregated across all tokens.<br />\n During the cooldown period, further requests may be rejected until the cooldown ends.<br />\n In rare cases, an endpoint may have a different rate limit than the default. When that happens, the differing limit will be explicitly stated in the API documentation for that endpoint.<br />\n <h5>Access Key</h5>In order to be able to access any API you have to create an access token.Therefore you have to follow these steps:-\n <ul>\n <li>Go to your profile page and click on Personal Access Tokens on the left side bar</li><li>Click on the 'Create Access Token' top right button in order to create a new access token.</li><li>Copy the created token and paste it into the field that pops up when you click on the 'Authorize' button </li><li>Note: you can copy the token only once.</li><li>Now you can easily access any API</li>\n </ul>\n <h5>V2 APIs</h5>We are currently working on upgrading all our APIs to v2, and we recommend that you use v2 APIs if it is available.\n <p>V2 APIs are faster, robust and more flexible than v1 APIs</p>"
basePath: /
schemes:
- https
- http
consumes:
- application/json
produces:
- application/json
tags:
- name: Tickets V2
description: All operations on PlanRadar tickets
paths:
/api/v2/{customer_id}/projects/{project_id}/tickets/journals:
get:
summary: Retrieve journals for all accessible tickets in a project
tags:
- Tickets V2
description: 'Retrieve journal entries for every ticket the caller can access within the given project,
in a single API call. Intended to replace individual `GET /tickets/{uuid}/journals` calls
during ERP sync integrations.
Counts as **1 API call**. Only returns journals from tickets the user has access to within
the specified project. Field-level and document permissions are respected. Each journal
entry includes `ticket-uuid` for mapping back to its ticket.
**Pagination:** default 50, max 100 (server-enforced). Results ordered chronologically.
**Typical sync flow:**
1. `GET /tickets/journals?last_sync_date=...` with the timestamp from your last sync (this endpoint)
2. Process changes, store new sync timestamp
'
security:
- apiKey: []
produces:
- application/json
parameters:
- name: customer_id
in: path
type: string
required: true
description: Encoded customer ID
example: ymjxnx
- name: project_id
in: path
type: string
required: true
description: Encoded project ID
example: xqwprl
- name: last_sync_date
in: query
required: false
type: string
description: ISO 8601 datetime. Only return journal entries created on or after this datetime. Use for incremental sync — pass the timestamp from your last successful sync.
example: '2026-03-20T10:15:00Z'
- name: prop_keys
in: query
required: false
type: array
items:
type: string
collectionFormat: csv
description: Comma-separated list of journal_details.prop_key values to filter by (e.g. status_id, assigned_to_id). When provided, only journals with a matching detail row are returned.
- name: page
in: query
required: false
type: integer
description: Page number (1-based). Defaults to 1.
example: 1
- name: pagesize
in: query
required: false
type: integer
description: 'Number of journal entries per page. Default: 50. Maximum: 100 (server-enforced, values above 100 are clamped).'
example: 50
responses:
'200':
description: Paginated — page and pagesize control the window
'401':
description: Unauthorized — missing or invalid API key
'404':
description: Caller is not a member of the project
/api/v2/{customer_id}/projects/{project_id}/tickets:
get:
summary: Returns all tickets
tags:
- Tickets V2
produces:
- application/json
description: "This API returns all tickets of a specific project. <br />\n Without any extra parameters it will return the first 100 tickets ordered by ascending ID. Extra parameters can be added to filter and sort the results.<br />\n <b>Ticket Sorting:</b>\n Tickets can be sorted by their attributes like component_id, priority_id etc. .\n The sort is always ascending unless the field name is prefixed with a minus (U+002D HYPHEN-MINUS, “-“) i.e. `-id` or `id`.\n <br /><b>Example: </b> `/tickets?sort=-component_id`<br />\n <b>Ticket Filtring:</b>\n You can use one of your created filters by providing its id to the `apply_filter` parameter. Moreover, you can use the on-fly filters.<br />\n <b>On-Fly Filters:</b>\n You can filter tickets based on the value of any of their attributes using structured filter parameters. Both simple and advanced formats are supported.<br />\n <b>Simple Format:</b> `filter[{field}]={value}` - Direct field-value filtering<br />\n <b>Advanced Format:</b> `filter[{field}][][predicate]={predicate}&filter[{field}][][value][]={value}` - Complex filtering with predicates<br />\n <b>Simple Format Examples:</b><br />\n • Filter by status: `/tickets?filter[status_id]=1` (Open tickets)<br />\n • Filter by priority: `/tickets?filter[priority_id]=3` (High priority)<br />\n • Multiple simple filters: `/tickets?filter[status_id]=1&filter[priority_id]=3`<br />\n <b>Advanced Format Examples:</b><br />\n • Filter by status with 'in' predicate: `/tickets?filter[status_id][][predicate]=in&filter[status_id][][value][]=1&filter[status_id][][value][]=2`<br />\n • Filter by multiple conditions: `/tickets?filter[status_id][][predicate]=in&filter[status_id][][value][]=1&filter[priority_id][][predicate]=eq&filter[priority_id][][value][]=3`<br />\n • Text search with 'like': `/tickets?filter[subject][][predicate]=like&filter[subject][][value][]=urgent`<br />\n • Date range filtering: `/tickets?filter[created_at][][predicate]=between&filter[created_at][][value][]=2024-01-01&filter[created_at][][value][]=2024-12-31`<br />\n <b>Available Predicates:</b> eq, neq, in, not_in, like, not_like, between, has, has_not, le, ge<br/>\n <b>Image Loading:</b>\n You can set load_images to true if you want the images of a ticket to be returned as well<br />\n <b>Status Dictionary:</b> [1 (lm)= Open, 2 (ol) = In-Progress, 3 (ma) = Resolved, 4 (ex) = Feedback, 5 (gk) = Closed, 6 (ky) = Rejected]\n <b>Priority Dictionary:</b> [1 = Low, 2 = Normal, 3 = High]\n\n "
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
description: Set project_id to `0` if you want to load the tickets of all projects
- name: page
in: query
type: integer
description: sets the number of pages for the returned tickets
- name: pagesize
in: query
type: integer
description: sets the number of tickets that should be returned per page <br/>Maximum is 500, default is 100
- name: last_sync_date
in: query
type: string
description: is a Unix Timestamp. If it is set, only tickets that were created after that timestamp will be returned
- name: sort
in: query
type: string
- name: filter[status_id]
in: query
type: string
description: "Simple status ID filter. Use status codes: 1=Open, 2=In-Progress, 3=Resolved, 4=Feedback, 5=Closed, 6=Rejected. Example: filter[status_id]=1 - Structured filter parameter supporting multiple conditions and predicates. You can use either simple or advanced format.<br/>\n <b>Status ID Mapping:</b> 1 (lm)=Open, 2 (ol)=In-Progress, 3 (ma)=Resolved, 4 (ex)=Feedback, 5 (gk)=Closed, 6 (ky)=Rejected<br/>\n <b>Simple Format:</b> filter[{field}]={value} - Example: filter[status_id]=1<br/>\n <b>Advanced Format:</b> filter[{field}][][predicate]={predicate}&filter[{field}][][value][]={value}<br/>\n <b>Supported Fields:</b> status_id, priority-id, assigned-to-id, author-id, subject, created-at, due-date, etc.<br/>\n <b>Predicates:</b> eq (equals), neq (not equals), in (in list), not_in (not in list), like (contains), not_like (not contains), between (range), has (has value), has_not (no value), le (less/equal), ge (greater/equal)<br/>\n <b>Examples:</b><br/>\n • Simple: filter[status_id]=1 for filtering tickets with status 1<br/>\n • Advanced: filter[status_id][][predicate]=in&filter[status_id][][value][]=1&filter[status_id][][value][]=2 for filtering tickets with status 1 or 2"
- name: apply_filter
in: query
type: string
- name: load_images
in: query
type: boolean
description: sets whether or not the images of a ticket should be returned
- name: can_have_parent
in: query
type: boolean
description: load only ticket that could have parent
- name: search
in: query
type: string
description: keyword, based on which the tickets will be filtered
responses:
'406':
description: Unsupported Accept Header
post:
summary: Creates ticket
tags:
- Tickets V2
description: "This API is used for creating a ticket.<br/>\n <b>customer-id</b>: Can be set to null in the request body to create a ticket without a customer association (used for form field attachment pre-upload tickets).<br/>\n The response includes <b>typed_values_metadata</b> which contains metadata about form field attachments (e.g. has_attachments per field_id).<br/><br/>\n <b>Note:</b> Do not modify the <code>meta-options</code> attribute - it is reserved for internal use.\n <br /><br />\n <b>Ticket Example:</b>\n <br />\n <pre>\n {\n \"data\": {\n \"attributes\": {\n \"uuid\": \"35b03889-e759-4ded-b848-24337eea10d2\",\n \"subject\": \"After the Banality Post-Painterly Art of Misfortune\",\n \"typed-values\": {\n \"tf37e04757dabbca63\": 33.52511960017461,\n \"tf601be06c9a1c46f7\": \"1989-06-05T23:16:06.412+00:00\",\n \"tfd4367f50e84a9408\": \"Arbitrary Sustainability Daring to Defy Dysfunction\"\n },\n \"assigned-to-id\": \"gpk\",\n \"status-id\": \"ma\",\n \"component-id\": \"nyk\",\n \"ticket-type-id\": \"obk\",\n \"customer-id\": \"ymjxnx\"\n }\n }\n }\n </pre>"
produces:
- application/json
security:
- apiKey: []
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: ticket
in: body
schema:
type: object
properties:
data:
type: object
properties:
attributes:
$ref: '#/definitions/Ticket'
responses:
'404':
description: Ticket Not Created
/api/v2/{customer_id}/projects/{project_id}/tickets/tickets_position:
get:
summary: Returns tickets positions
tags:
- Tickets V2
produces:
- application/json
description: This API returns all tickets positions of a specific project without pagination.
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
responses:
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/export/xls:
post:
summary: Exports filtered tickets as Excel file
tags:
- Tickets V2
description: ' This API is used for exporting tickets as an Excel file based on their filter '
produces:
- application/json
security:
- apiKey: []
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: pagesize
in: query
type: integer
description: Maximum is 500, default is 100
- name: last_sync_date
in: query
type: string
- name: sort
in: query
type: string
- name: filter
in: query
type: string
- name: apply_filter
in: query
type: string
description: filterid, based on which the tickets should be filtered
- name: search
in: query
type: string
description: keyword, based on which the tickets should be filtered
- name: async
in: query
type: boolean
description: Set to true to force asynchronous (background) processing. When enabled, the response returns a job ID instead of the file directly.
responses:
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/{uuid}/single_ticket_export:
get:
summary: Exports specific ticket
tags:
- Tickets V2
produces:
- application/json
description: This API is used for exporting a specific ticket based on its uuid.
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: uuid
in: path
type: string
description: unique identifier of the ticket
required: true
- name: pagesize
in: query
type: integer
description: Maximum is 500, default is 100
- name: last_sync_date
in: query
type: string
- name: timezone_offset
in: query
type: integer
description: ' -timestamp, is the difference between local time and the worlwide UTC time '
- name: async
in: query
type: boolean
description: Set to true to force asynchronous (background) processing. When enabled, the response returns a job ID instead of the file directly.
responses:
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/{uuid}:
get:
summary: Returns specific ticket
description: "This API returns a specific ticket based on its uuid.<br/>\n The response includes a <b>typed_values_metadata</b> attribute, which is a JSON object containing metadata about typed field values.\n For form field attachment fields, it includes <code>has_attachments</code> per field_id.<br/>\n Example: <code>{ \"ft123456789\": { \"has_attachments\": true } }</code><br/><br/>\n <b>Note:</b> Do not modify the <code>meta-options</code> attribute - it is reserved for internal use."
tags:
- Tickets V2
produces:
- application/json
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: uuid
in: path
type: string
description: unique identifier of the ticket
required: true
responses:
'404':
description: Ticket Not Found
'406':
description: Unsupported Accept Header
delete:
summary: Deletes specific ticket
tags:
- Tickets V2
produces:
- application/json
description: This API is used for deleting a specific ticket based on its uuid.
security:
- apiKey: []
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: uuid
in: path
type: string
description: unique identifier of the ticket
required: true
responses:
'406':
description: Unsupported Accept Header
put:
summary: Updates specific ticket
tags:
- Tickets V2
description: "This API is used for updating the details of a specific ticket based on its uuid.<br/>\n The response includes <b>typed_values_metadata</b> which contains metadata about form field attachments (e.g. has_attachments per field_id).<br/><br/>\n <b>Note:</b> Do not modify the <code>meta-options</code> attribute - it is reserved for internal use."
produces:
- application/json
security:
- apiKey: []
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: uuid
in: path
type: string
description: unique identifier of the ticket
required: true
- name: clean_up_assets
in: query
type: boolean
description: If set to true, triggers a background job to remove any orphaned assets (images, documents, audios, videos) that no longer belong to the ticket
- name: ticket
in: body
schema:
type: object
properties:
data:
type: object
properties:
attributes:
$ref: '#/definitions/Ticket'
required:
- title
- content
responses:
'404':
description: Ticket Not found
/api/v2/{customer_id}/projects/{project_id}/tickets/{uuid}/mark_as_seen:
get:
summary: Marks specific ticket as seen
tags:
- Tickets V2
description: 'This API is used for marking a specific ticket as seen if the request is done by the ticket asignee, else it returns whether or not the ticket was marked as seen. '
produces:
- application/json
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: uuid
in: path
type: string
description: unique identifier of the ticket
required: true
responses:
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/export/pdf:
post:
summary: Exports filtered tickets as PDF
tags:
- Tickets V2
description: ' This API is used for exporting tickets to pdf files based on their filter. '
security:
- apiKey: []
produces:
- application/json
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: pagesize
in: query
type: integer
description: Maximum is 500, default is 100
- name: last_sync_date
in: query
type: string
- name: sort
in: query
type: string
- name: filter[status_id]
in: query
type: string
description: "Simple status ID filter. Use status codes: 1=Open, 2=In-Progress, 3=Resolved, 4=Feedback, 5=Closed, 6=Rejected. Example: filter[status_id]=1 - Structured filter parameter supporting multiple conditions and predicates. You can use either simple or advanced format.<br/>\n <b>Status ID Mapping:</b> 1 (lm)=Open, 2 (ol)=In-Progress, 3 (ma)=Resolved, 4 (ex)=Feedback, 5 (gk)=Closed, 6 (ky)=Rejected<br/>\n <b>Simple Format:</b> `filter[{field}]={value}` - Direct field-value filtering<br/>\n <b>Advanced Format:</b> `filter[{field}][][predicate]={predicate}&filter[{field}][][value][]={value}` - Complex filtering with predicates<br/>\n <b>Supported Fields:</b> status_id, priority-id, assigned-to-id, author-id, subject, created-at, due-date, etc.<br/>\n <b>Predicates:</b> eq (equals), neq (not equals), in (in list), not_in (not in list), like (contains), not_like (not contains), between (range), has (has value), has_not (no value), le (less/equal), ge (greater/equal)<br/>\n <b>Simple Format Examples:</b><br/>\n • Filter by status: `filter[status_id]=1` (Open tickets)<br/>\n • Filter by priority: `filter[priority-id]=3` (High priority)<br/>\n • Multiple simple filters: `filter[status_id]=1&filter[priority-id]=3`<br/>\n <b>Advanced Format Examples:</b><br/>\n • Status filtering with 'in': `filter[status_id][][predicate]=in&filter[status_id][][value][]=1&filter[status_id][][value][]=2`<br/>\n • Text search with 'like': `filter[subject][][predicate]=like&filter[subject][][value][]=urgent`<br/>\n • Date range filtering: `filter[created-at][][predicate]=between&filter[created-at][][value][]=2024-01-01&filter[created-at][][value][]=2024-12-31`<br/>\n • Complex multiple conditions: `filter[status_id][][predicate]=eq&filter[status_id][][value][]=1&filter[priority-id][][predicate]=eq&filter[priority-id][][value][]=3`"
- name: apply_filter
in: query
type: string
description: filterid, based on which the tickets should be filtered
- name: search
in: query
type: string
description: keyword, based on which the tickets should be filtered
- name: async
in: query
type: boolean
description: Set to true to force asynchronous (background) processing. When enabled, the response returns a job ID instead of the file directly.
responses:
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/load_recurring_reminders:
get:
summary: Returns all recurring reminders
tags:
- Tickets V2
description: This API returns all recurring reminders of the tickets of the logged in customer.
produces:
- application/json
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: page
in: query
type: integer
description: sets the number of pages for the returned reminders
- name: pagesize
in: query
type: integer
description: sets the number of reminders that should be returned per page <br> Maximum is 500, default is 100
- name: last_sync_date
in: query
type: string
description: is a Unix Timestamp. If it is set, only reminders that were created after that timestamp will be returned
responses:
'406':
description: unsupported accept header
/api/v2/{customer_id}/projects/{project_id}/tickets/ticket_types_stats:
get:
summary: Returns the count of tickets per ticket type
tags:
- Tickets V2
description: "This API returns the count of tickets per ticket types. Extra parameters can be added to filter the results.\n <br> <b>Ticket Filtering </b>\n You can filter the results by one of your created filters by providing its id to the apply_filter parameter.\n <br><b>Example: </b>ticket_types_stats?apply_filter=jpg\n <br>Moreover, the results can be filtered based on any condition of the related model.\n filter[author_id]=1 will list all tickets related to this author id.\n filter[author_email]=1 will list all tickets related to this author email.\n Multiple filters can be used at the same time.\n <br><b>Example: </b> filter[id][]=PN&filter[id][]=nE\n <br><b>Available Models</b>: customer, ticket_type, project, component, status, ticket, author, assigned_to, updated_by, due_by, overdue_by, creation_period"
produces:
- application/json
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: filter
in: query
type: string
- name: apply_filter
in: query
type: string
responses:
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/batch_update:
put:
summary: Updates multiple tickets at once
tags:
- Tickets V2
description: "This API is used for updating multiple tickets at once based on the provided filteration. However, no filter will update all the data of the project.\n <br />\n <b>Filtering </b>\n You can filter the tickets by one of your created filters by providing its id to the apply_filter attribute.\n Moreover, the tickets can be filtered based on any condition of the related model. These have to be set in the filter object of the body.\n Multiple filters can be used at the same time.\n <br />\n Example:\n <br />\n\n {\n \"apply_filter\":\"mod\",\n \"filter\":{\n \"uuid\":[\n {\n \"predicate\":\"in\",\n \"value\":[\n \"b404f669-e02a-4273-bcff-aac72b31386f\",\n \"fa30521c-bf68-4246-b50f-29fd9c91ee5f\"\n ]\n }\n ]\n },\n \"data\":{\n \"attributes\":{\n \"priority-id\":\"ol\"\n },\n \"obk\":{\n \"tf37e04757dabbca63\":\"4\",\n \"tf7ae8b29ea4b4c1e9\":[\n \"makw\"\n ],\n \"typed-values-metadata\":{\n \"tf37e04757dabbca63\":{\n \"note\":\"observation details\",\n \"has_attachments\":true\n }\n }\n }\n }\n }\n\n <br /><br />\n <b>CheckList Fields in Batch Update</b><br />\n When updating CheckList fields (CustomFields::CheckListType), typed-values-metadata must be placed inside the ticket_type_id bucket alongside the typed values. The server extracts it automatically.<br />\n <b>Silent Skip Behavior:</b> If a checklist field requires a mandatory note or attachment and the metadata is not provided, the checklist field update is silently skipped for that ticket. Other fields in the same batch update are still applied normally."
x-websocket-response:
channel: user_{customerId}_{userId}
message_type: cable-events
payload:
type: object
properties:
multiupdate-progress:
type: integer
description: Always 1, signals completion
failed-count:
type: integer
description: Number of tickets that failed to update
produces:
- application/json
security:
- apiKey: []
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: multi_update_object
in: body
schema:
type: object
properties:
apply_filer:
type: string
data:
type: object
properties:
attributes:
type: object
properties:
field-name:
type: string
description: 'the key should be set to the field name of the field that should be changed <br\> -- the value of the field name is the new value that the user wants to set - example priority-id: "ol"'
ticket_type_id:
type: object
properties:
typed-field-id:
type: string
description: -- typed values have to be grouped by the ticket types, the type of their values depends on the field type
typed-values-metadata:
type: object
description: Metadata for checklist fields (notes, attachment flags). Keys are typed field IDs, values are objects with optional 'note' (string) and 'has_attachments' (boolean)
filter:
type: object
properties:
uuid:
type: array
items:
type: string
component_id:
type: string
description: -is optional
description: ' filter contains the filter data and an array of all the uuids of the tickets that should be updated <br /> a filter consists of a filter field key, a predicate and a value - there can be multiple field keys and values'
required:
- title
- content
responses:
'200':
description: Tickets Updated
schema:
type: object
properties:
id:
type: integer
subject:
type: string
required:
- id
- subject
'406':
description: Unsupported Accept Header
/api/v2/{customer_id}/projects/{project_id}/tickets/multi_update:
put:
summary: Updates multiple tickets with array
tags:
- Tickets V2
description: This API is used for updating multiple tickets at once based on the uuid.
x-websocket-response:
channel: user_{customerId}_{userId}
message_type: cable-events
payload:
type: object
properties:
multiupdate-progress:
type: integer
description: Always 1, signals completion
failed-count:
type: integer
description: Number of tickets that failed to update
produces:
- application/json
security:
- apiKey: []
parameters:
- name: customer_id
in: path
type: string
required: true
- name: project_id
in: path
type: string
required: true
- name: multi_update_object
in: body
schema:
type: object
properties:
data:
type: array
items:
type: object
properties:
attributes:
type: object
properties:
uuid:
type: string
description: The unique identifier of the ticket
subject:
type: string
description: Ticket title
parent-id:
type: string
description: UUID of the parent ticket
status-id:
type: string
description: The status of the ticket
priority-id:
type: string
description: The priority of the ticket
assigned-to-id:
type: string
description: The id of the assignee
assignee-type:
type: string
description: User OR DefectradarGroup
due-date:
type: string
description: Ticket due date
closed-at:
type: string
description: Ticket closed at date
extension-date:
type: string
description: Extension date
ty
# --- truncated at 32 KB (57 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/planradar/refs/heads/main/openapi/planradar-tickets-v2-api-openapi.yml