Freshdesk API 티켓

Freshdesk API 티켓

고객센터를 운영하다 보면 "이 문의를 API로 자동으로 접수하고 싶다", "오늘 들어온 티켓을 코드로 한 번에 정리하고 싶다" 같은 요구가 꼭 생겨요. Freshdesk의 티켓 API는 바로 그런 일을 처리하는 핵심 축입니다. 티켓 생성부터 조회·필터·수정까지 모두 REST 엔드포인트 하나로 다룰 수 있어서, 외부 시스템에서 문의가 들어왔을 때 티켓을 자동 생성하거나 대시보드처럼 티켓 목록을 가져오는 데 아주 유용해요.

이 문서는 Freshdesk 공식 API 중 티켓(Tickets) 부분을 발췌해 정리한 내용입니다. 요청·응답 구조와 코드 예시는 원문 그대로 실었으니, 실제 호출해 보면서 하나씩 따라 해 보면 좋아요. 인증은 -u yourapikey:***처럼 API 키를 기본 인증으로 넘기는 방식이며, domain 자리에는 본인 계정의 서브도메인이 들어가요.

출처: 문서

티켓이란 무엇인가요

티켓은 요청자(requester)가 올린, 해결이 필요한 이슈예요. 보안 취약점처럼 긴급하고 우선순위가 높은 문제일 수도 있고, 무료 티셔츠에 관한 질문처럼 낮은 우선순위의 문의일 수도 있어요. 티켓은 담당 에이전트의 전문성과 티켓 주제에 따라 에이전트에게 배정됩니다.

티켓의 주요 속성 (Ticket Properties)

티켓 하나에는 아래 같은 필드들이 담겨요. 이 중 어떤 것은 읽기 전용이고, 어떤 것은 생성·수정 시에 함께 보내는 요청 필드예요.

Attribute Type Description
attachments array of objects 티켓 첨부파일. 총 크기는 20MB를 초과할 수 없어요.
cc_emails array of strings 수신 티켓 메일의 'cc' 필드에 추가된 이메일 주소
company_id number 이 티켓이 속한 회사의 ID
custom_fields dictionary 커스텀 필드의 이름과 값을 담은 키-값 쌍
description string 티켓의 HTML 내용
description_text string 티켓 내용을 평문(plain text)으로 나타낸 값
due_by datetime 티켓이 해결되어야 하는 시한 타임스탬프
email string 요청자의 이메일 주소. Freshdesk에 이 이메일의 연락처가 없다면 새 연락처로 추가돼요.
group_id number 티켓이 배정된 그룹의 ID
id number 티켓의 고유 ID
name string 요청자의 이름
phone string 요청자의 전화번호
priority number 티켓의 우선순위
product_id number 티켓과 연결된 제품의 ID
requester_id number 요청자의 사용자 ID
responder_id number 티켓이 배정된 에이전트의 ID
source number 티켓이 생성된 채널
spam boolean 티켓이 스팸으로 표시되었으면 true
status number 티켓의 상태
subject string 티켓의 제목
tags array of strings 티켓과 연결된 태그
created_at datetime 티켓 생성 타임스탬프
updated_at datetime 티켓 업데이트 타임스탬프

상태·우선순위·소스의 고정 수치

모든 티켓은 상태(Status), 우선순위(Priority), 소스(Source)를 고정된 숫자값으로 표현해요. 정확한 값이 아래 표에 정리되어 있어요.

Status (상태)

Status Value
Open 2
Pending 3
Resolved 4
Closed 5

Priority (우선순위)

Priority Value
Low 1
Medium 2
High 3
Urgent 4

Source (소스)

Source Value
Email 1
Portal 2
Phone 3
Chat 7
Feedback Widget 9
Outbound Email 10

티켓 생성 (POST /api/v2/tickets)

새 티켓을 만들 때는 POST /api/v2/tickets 엔드포인트를 사용해요. 요청자 식별은 email, phone, twitter_id, facebook_id, requester_id, unique_external_id어느 하나가 필수예요. 기본 status 값은 2(Open), priority 기본값은 1(Low), source 기본값은 2(Portal)예요.

curl 예시부터 볼게요.

curl -v -u yourapikey:*** -H "Content-Type: application/json" -d '{  "description": "Details about the issue...", "subject": "Support Needed...", "email": "[email protected]", "priority": 1, "status": 2, "cc_emails": ["[email protected]","[email protected]"] }' -X POST 'https://domain.freshdesk.com/api/v2/tickets'

요청 본문(request)은 이렇게 생겼어요.

{
  "description":"Some details on the issue ...",
  "subject":"Support needed..",
  "email":"[email protected]",
  "priority":1,
  "status":2,
  "cc_emails":["[email protected]", "[email protected]"]
}

그러면 응답(response)으로 생성된 티켓 객체가 돌아와요. id가 배정되고, due_by·fr_due_by 같은 시한 값은 티켓 생성 후 몇 초 안에 채워져요.

{
  "cc_emails" : ["[email protected]", "[email protected]"],
  "fwd_emails" : [ ],
  "reply_cc_emails" : ["[email protected]", "[email protected]"],
  "email_config_id" : null,
  "group_id" : null,
  "priority" : 1,
  "requester_id" : 129,
  "responder_id" : null,
  "source" : 2,
  "status" : 2,
  "subject" : "Support needed..",
  "company_id" : 1,
  "id" : 1,
  "type" : "Question",
  "to_emails" : null,
  "product_id" : null,
  "fr_escalated" : false,
  "spam" : false,
  "urgent" : false,
  "is_escalated" : false,
  "created_at" : "2015-07-09T13:08:06Z",
  "updated_at" : "2015-07-23T04:41:12Z",
  "due_by" : "2015-07-14T13:08:06Z",
  "fr_due_by" : "2015-07-10T13:08:06Z",
  "description_text" : "Some details on the issue ...",
  "description" : "<div>Some details on the issue ..</div>",
  "tags" : [ ],
  "attachments" : [ ]
}

커스텀 필드와 함께 생성하기

계정에 만든 커스텀 필드가 있다면 custom_fields에 키-값으로 실어서 보내면 돼요.

curl -v -u yourapikey:*** -H "Content-Type: application/json" -d '{  "description": "Details about the issue...", "subject": "Support Needed...", "email": "[email protected]", "priority": 1, "status": 2, "cc_emails": ["[email protected]","[email protected]"], "custom_fields" : { "category" : "Primary" } }' -X POST 'https://domain.freshdesk.com/api/v2/tickets'

응답에서 custom_fields 값이 그대로 반영된 걸 확인할 수 있어요.

{
  "cc_emails" : ["[email protected]", "[email protected]"],
  "fwd_emails" : [ ],
  "reply_cc_emails" : ["[email protected]", "[email protected]"],
  "email_config_id" : null,
  "group_id" : null,
  "priority" : 1,
  "requester_id" : 129,
  "responder_id" : null,
  "source" : 2,
  "status" : 2,
  "subject" : "Support needed..",
  "company_id" : 1,
  "id" : 1,
  "type" : "Question",
  "to_emails" : null,
  "product_id" : null,
  "fr_escalated" : false,
  "spam" : false,
  "urgent" : false,
  "is_escalated" : false,
  "created_at" : "2015-07-09T13:08:06Z",
  "updated_at" : "2015-07-23T04:41:12Z",
  "due_by" : "2015-07-14T13:08:06Z",
  "fr_due_by" : "2015-07-10T13:08:06Z",
  "description_text" : "Some details on the issue ...",
  "description" : "<div>Some details on the issue ..</div>",
  "custom_fields" : {
    "category" : "Primary"
  },
  "tags" : [ ],
  "attachments" : [ ]
}

첨부파일과 함께 생성하기

첨부파일이 있으면 Content-Typemultipart/form-data로 설정하고 attachments[] 필드로 보내야 해요.

curl -v -u yourapikey:*** -F "attachments[]=@/path/to/attachment1.ext" -F "attachments[]=@/path/to/attachment2.ext" -F "[email protected]" -F "subject=Ticket Title" -F "description=this is a sample ticket" -X POST 'https://domain.freshdesk.com/api/v2/tickets'

첨부는 응답의 attachments 배열에 파일 메타데이터로 나타나요.

{
  "cc_emails" : ["[email protected]", "[email protected]"],
  "fwd_emails" : [ ],
  "reply_cc_emails" : ["[email protected]", "[email protected]"],
  "email_config_id" : null,
  "group_id" : null,
  "priority" : 1,
  "requester_id" : 129,
  "responder_id" : null,
  "source" : 2,
  "status" : 2,
  "subject" : "Ticket Title",
  "id" : 1,
  "type" : "Question",
  "to_emails" : null,
  "product_id" : null,
  "fr_escalated" : false,
  "spam" : false,
  "urgent" : false,
  "is_escalated" : false,
  "created_at" : "2015-07-09T13:08:06Z",
  "updated_at" : "2015-07-23T04:41:12Z",
  "due_by" : "2015-07-14T13:08:06Z",
  "fr_due_by" : "2015-07-10T13:08:06Z",
  "description_text" : "this is a sample ticket",
  "description" : "<div>this is a sample ticket</div>",
  "custom_fields" : {
    "category" : null
  },
  "tags" : [ ],
  "attachments":[
    {
      "id":4004881085,
      "content_type":"image/jpeg",
      "file_size":44115,
      "name":"attachment1.jpg",
      "attachment_url":"https://cdn.freshdesk.com/data/helpdesk/attachments/production/4004881085/original/attachment.jpg"
      "created_at":"2014-07-28T16:20:03+05:30",
      "updated_at":"2014-07-28T16:20:03+05:30",
    },
    {
      "id":4004881086,
      "content_type":"image/jpeg",
      "file_size":44134,
      "name":"attachment2.jpg",
      "attachment_url":"https://cdn.freshdesk.com/data/helpdesk/attachments/production/4004881085/original/attachment2.jpg"
      "created_at":"2014-07-28T16:20:03+05:30",
      "updated_at":"2014-07-28T16:20:03+05:30",
    }
  ]
}

티켓 조회 (GET /api/v2/tickets/[id])

하나의 티켓을 조회할 때는 GET /api/v2/tickets/[id]를 사용해요. 기본적으로 conversations, company name, requester email 같은 일부 필드는 응답에 포함되지 않아요. 이런 값들이 필요하면 include 파라미터로 추가 정보를 임베드(embed)하면 돼요. 각 include는 추가 API 크레딧을 소모한다는 점을 기억하세요.

curl -v -u yourapikey:*** -H "Content-Type: application/json"  -X GET 'https://domain.freshdesk.com/api/v2/tickets/20'

include로 담을 수 있는 항목은 아래와 같아요.

Embed Handle
conversations /api/v2/tickets/[id]?include=conversations — 생성 순(ascending)으로 최대 10개의 대화를 반환해요. conversations include는 API 호출 2회를 소모해요. 10개를 넘는 대화는 List All Conversations of a Ticket API로 조회해요.
requester /api/v2/tickets/[id]?include=requester — 요청자의 email, id, mobile, name, phone을 반환해요.
company /api/v2/tickets/[id]?include=company — 회사의 id와 name을 반환해요.
stats /api/v2/tickets/[id]?include=stats — 티켓의 closed_at, resolved_at, first_responded_at 시각을 반환해요.
curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets/20?include=conversations'
curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets/20?include=company,requester'
curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets/20?include=stats'

티켓 목록 조회 (GET /api/v2/tickets)

여러 티켓을 한 번에 가져올 때는 GET /api/v2/tickets를 사용해요. 기본적으로 삭제되지 않았고 스팸으로 표시되지 않은 티켓만 반환하며, 필터를 쓰면 특정 조건에 맞는 티켓만 고를 수 있어요.

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets'

목록 조회 시 알아둘 점

기본 동작 몇 가지를 짚고 갈게요.

  • 기본적으로 최근 30일 내에 생성된 티켓만 반환돼요. 더 오래된 티켓은 updated_since 필터를 사용해요.
  • 최대 300페이지(30,000개 티켓)까지만 반환돼요.
  • 필터를 쓸 때는 쿼리 문자열을 URL 인코딩해야 해요.
  • include를 쓰면 각 include가 2크레딧을 추가로 소모해요 (예: stats include → 총 3크레딧).
  • 2018-11-30 이후에 생성된 계정은 description을 얻으려면 include를 사용해야 해요.

필터 (Filter by)

Filter by Handle
Predefined filters /api/v2/tickets?filter=[filter_name] — 사용 가능한 필터는 new_and_my_open, watching, spam, deleted예요.
Requester /api/v2/tickets?requester_id=[id], /api/v2/tickets?email=[requester_email], /api/v2/tickets?unique_external_id=[requester_unique_external_id]
Company ID /api/v2/tickets?company_id=[id]
Updated since /api/v2/tickets?updated_since=2015-01-19T02:00:00Z
Custom ticket views Filter Tickets API 참고

정렬 (Sort by)

Sort by Handle
created_at, due_by, updated_at, status /api/v2/tickets?order_by=created_at — 기본 정렬은 created_at
asc, desc /api/v2/tickets?order_type=asc — 기본 정렬 방향은 desc

필터·정렬·페이지네이션 예시

requester_id=1230인 티켓을 상태 기준 내림차순으로 정렬해 볼게요.

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets?requester_id=1230&order_by=status&order_type=desc'

한 페이지에 10개씩 2페이지를 조회하는 페이지네이션 예시예요.

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets?per_page=10&page=2'

특정 시점 이후 업데이트된 티켓만 가져올 수도 있어요.

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/tickets?updated_since=2015-08-17'

티켓 목록 응답은 티켓 객체들의 배열로 돌아와요.

[
  {
    "cc_emails" : ["[email protected]", "[email protected]"],
    "fwd_emails" : [ ],
    "reply_cc_emails" : ["[email protected]", "[email protected]"],
    "fr_escalated" : false,
    "spam" : false,
    "email_config_id" : null,
    "group_id" : 2,
    "priority" : 1,
    "requester_id" : 5,
    "responder_id" : 1,
    "source" : 2,
    "source_info": 1,
    "status" : 2,
    "subject" : "Please help",
    "to_emails" : null,
    "product_id" : null,
    "id" : 18,
    "type" : Lead,
    "created_at" : "2015-08-17T12:02:50Z",
    "updated_at" : "2015-08-17T12:02:51Z",
    "due_by" : "2015-08-20T11:30:00Z",
    "fr_due_by" : "2015-08-18T11:30:00Z",
    "is_escalated" : false,
    "structured_description" : {
      "description_contents" : [
        {
          "type" : "text",
          "data" : {
            "content" : "Please help with this issue"
          }
        }
      ]
    },
    "custom_fields" : {
      "category" : "Default"
    }
  },
  {
    "cc_emails" : [ ],
    "fwd_emails" : [ ],
    "reply_cc_emails" : [ ],
    "fr_escalated" : false,
    "spam" : false,
    "email_config_id" : null,
    "group_id" : null,
    "priority" : 1,
    "requester_id" : 1,
    "responder_id" : null,
    "source" : 2,
    "source_info": 1,
    "status" : 2,
    "subject" : "",
    "to_emails" : null,
    "product_id" : null,
    "id" : 17,
    "type" : null,
    "created_at" : "2015-08-17T12:02:06Z",
    "updated_at" : "2015-08-17T12:02:07Z",
    "due_by" : "2015-08-20T11:30:00Z",
    "fr_due_by" : "2015-08-18T11:30:00Z",
    "is_escalated" : false,
    "structured_description" : {
      "description_contents" : [
        {
          "type" : "text",
          "data" : {
            "content" : "Another ticket description"
          }
        }
      ]
    },
    "custom_fields" : {
      "category" : null
    }
  }
]

티켓 필터/검색 (GET /api/v2/search/tickets?query=[query])

계정에 만든 커스텀 티켓 필드를 기준으로 티켓을 걸러낼 때는 검색 API를 사용해요. 쿼리 형식은 "(ticket_field:integer OR ticket_field:'string') AND ticket_field:boolean" 형태예요.

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="priority:3"'

검색/필터 규칙

  • 보관(archived)된 티켓은 결과에 포함되지 않아요.
  • 쿼리는 반드시 URL 인코딩해야 해요.
  • 필드명은 대소문자를 구분해요.
  • 쿼리는 큰따옴표로 감싸야 하고, 최대 512자까지 사용할 수 있어요.
  • AND, OR 논리 연산자와 괄호 ()로 조건을 묶을 수 있어요.
  • 날짜·숫자 필드에는 :>(크거나 같음), :<(작거나 같음) 비교 연산자를 쓸 수 있어요.
  • 날짜 필드 입력은 UTC 형식이어야 해요.
  • 페이지당 30개 객체를 반환하고, 총 개수도 함께 반환돼요.
  • 페이지 파라미터로 넘기며, 페이지는 1부터 10을 초과하면 안 돼요.
  • 값이 없는 필드를 걸러낼 때는 null 키워드를 사용해요.
  • 변경 사항이 API에 반영되기까지 몇 분 소요될 수 있어요.

지원되는 검색 필드 (Supported Ticket Fields)

Field Type Description
agent_id integer 티켓이 배정된 에이전트의 ID
group_id integer 티켓이 배정된 그룹의 ID
priority integer 티켓의 우선순위
status integer 티켓의 상태
tag string 티켓에 연결된 태그
type string 티켓에 연결된 이슈 유형
due_by date 티켓이 해결되어야 하는 날짜 (YYYY-MM-DD)
fr_due_by date 첫 응답이 마감인 날짜 (YYYY-MM-DD)
created_at date 티켓 생성일 (YYYY-MM-DD)
updated_at date 티켓이 마지막으로 업데이트된 날짜 (YYYY-MM-DD)
closed_at date 티켓이 닫힌 날짜 (YYYY-MM-DD)

커스텀 필드도 검색에 사용할 수 있어요. Single line text(문자열), Number(정수), Checkbox(불리언), Dropdown(문자열) 타입을 지원해요.

검색 예시

여러 조건을 조합하는 예시를 몇 개 볼게요.

우선순위 4 또는 3인 티켓 검색:

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="priority:4%20OR%20priority:3"'

상태 3 또는 4인 티켓을 2페이지로 검색:

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="status:3%20OR%20status:4"&page=2'

우선순위가 3보다 크고 그룹 11에 배정된 열린 티켓:

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="priority:>3%20AND%20group_id:11%20AND%20status:2"'

커스텀 필드를 조합한 예시 (and + or):

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="(cf_sector:%27finance%27%20OR%20cf_sector:%27marketing%27)%20AND%20cf_locked:true"'

날짜 필드 비교:

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="priority:>3%20AND%20created_at:%272017-01-01%27"'

값이 없는 필드(null) 검색:

curl -v -u yourapikey:*** -X GET 'https://domain.freshdesk.com/api/v2/search/tickets?query="tag:null"'

티켓 수정 (PUT /api/v2/tickets/[id])

기존 티켓을 수정할 때는 PUT /api/v2/tickets/[id]를 사용해요. 바꾸고 싶은 필드만 payload로 보내면 돼요. 단, 일반 티켓과 달리 아웃바운드(outbound) 티켓의 subject와 description은 수정할 수 없어요.

curl -v -u yourapikey:*** -H "Content-Type: application/json" -X PUT -d '{ "priority":2, "status":3 }' 'https://domain.freshdesk.com/api/v2/tickets/1'

수정 시 사용할 수 있는 주요 파라미터는 생성과 거의 같아요. status(기본값 2), priority(기본값 1), source(기본값 2)는 표에 있는 고정 수치 중에서만 사용할 수 있어요. due_by·fr_due_by는 최신 SLA 버전 계정에서 티켓 업데이트 후 몇 초 안에 재계산되며, 응답에는 업데이트 전 값이 표시돼요.

더 알아보기 (Learn more)