{"currentRevision":"2026-10-23.1","compatibilityPolicy":{"versioning":"unversioned","breakingChanges":"allowed","tracking":"changelog-and-response-headers","title":"하위호환성 정책: Breaking change 허용","summary":"그랜터 Public API는 현재 별도 버전 경로를 운영하지 않으며, 필요한 API 계약 변경에는 breaking change를 허용합니다.","guidance":"사용자에게 영향을 주는 계약 변경은 이 changelog와 응답의 Granter-API-Revision·Granter-API-Changes 헤더에 기록합니다. 연동 클라이언트는 리비전 변경과 breaking 항목의 migration 안내를 확인해 반영해야 합니다."},"revisions":[{"revision":"2026-10-23.1","releasedAt":"2026-10-23","changes":[{"id":"tickets-attachments-create-added","type":"new","effectiveAt":"2026-10-23","endpointKeys":["tickets-attachments-create"],"summary":"티켓 첨부 업로드 API(`POST /api/public-docs/tickets/attachments`, `multipart/form-data`)를 추가합니다. `ticketId`·`ticketType`과 파일 1개(4MB까지)를 보내면 티켓에 첨부하고 새 첨부 ID를 돌려줍니다. 같은 이름·같은 내용(SHA-256)의 첨부가 이미 있으면 다시 올리지 않고 기존 첨부를 `duplicate: true`로 돌려줍니다."}]},{"revision":"2026-10-20.1","releasedAt":"2026-10-20","changes":[{"id":"tax-invoices-modify-issue-return-reason","type":"additive","effectiveAt":"2026-10-20","endpointKeys":["tax-invoices-modify-issue"],"summary":"세금계산서 수정발행(`POST /api/public-docs/tax-invoices/modify-issue`)의 `modificationOption.modificationReason`에 `환입`(반품)을 허용합니다. 반품된 금액만큼만 음수로 1장 발행하며, `supplyValue`·`totalAmount`가 음수이고 `tradeItems`에 음수 금액 항목이 있어야 합니다(아니면 400). 기존 사유(`기재사항 착오정정`·`공급가액 변동`)의 동작은 그대로입니다."}]},{"revision":"2026-10-19.1","releasedAt":"2026-10-19","changes":[{"id":"contracts-create-beta-added","type":"new","effectiveAt":"2026-10-19","endpointKeys":["contracts-create"],"summary":"[베타] 계약 생성·발급 API(`POST /api/public-docs/contracts/create`)를 추가합니다. 계약과 품목을 만들고 견적서·청구서·거래명세서를 발급한 뒤 지정한 이메일·알림톡으로 전달합니다. 베타 참여 회사에만 열리며(그 밖에는 403 `CONTRACT_CREATE_BETA_NOT_ENABLED`), `Idempotency-Key` header가 필수입니다. 계약서(전자서명)는 아직 만들 수 없습니다."},{"id":"contracts-tax-invoices-link-added","type":"new","effectiveAt":"2026-10-19","endpointKeys":["contracts-tax-invoices-link","contracts-tax-invoices-unlink"],"summary":"계약 세금계산서 연결·해제 API(`/api/public-docs/contracts/tax-invoices` POST·DELETE)를 추가합니다. `tax-invoices-issue` 응답의 `id`(원본 세금계산서 ID)로 연결하며, 티켓이 아직 없으면 대기(`pending`)로 받아 두었다가 자동 연결합니다."}]},{"revision":"2026-10-15.1","releasedAt":"2026-10-15","changes":[{"id":"purchase-usage-fee-included-shinhan","type":"behavioral","effectiveAt":"2026-10-15","endpointKeys":["tickets"],"summary":"`EXPENSE_TICKET` 응답의 `purchaseUsage.isFeeAmountIncluded`를 문서화하고, 신한카드 매입내역을 삼성카드와 같이 `true`로 제공합니다. 신한카드 해외 매입 금액은 카드사가 해외 이용수수료를 이미 합산해 내려주지만 수수료를 분리해 주지 않아 `feeAmount`가 0이고 플래그가 `false`로 나가 \"수수료 미포함\"으로 읽혔습니다. `isFeeAmountIncluded=true`면 `amount`가 수수료를 포함한 최종 금액이니 `feeAmount`를 다시 더하지 마세요."}]},{"revision":"2026-10-14.1","releasedAt":"2026-10-14","changes":[{"id":"contact-managers-api-added","type":"new","effectiveAt":"2026-10-14","endpointKeys":["contacts-managers-create","contacts-managers-update","contacts-managers-delete"],"summary":"거래처 담당자 추가·수정·삭제 API(`/api/public-docs/contacts/managers` POST·PUT·DELETE)를 추가합니다. 추가는 거래처를 다시 조회해 새 담당자 ID(`contactManagerId`)를 돌려주고, 하나로 특정하지 못하면 추측하지 않고 `idConfirmed=false`로 알립니다. 수정은 전달한 필드만 바꾸며, 수정·삭제는 다시 조회한 결과(`verified`)를 함께 돌려줍니다."},{"id":"contact-create-managers-added","type":"additive","effectiveAt":"2026-10-14","endpointKeys":["contacts-create"],"summary":"거래처 생성에 선택 필드 `managers`(최대 10명)를 추가합니다. 거래처를 만든 뒤 담당자를 순서대로 추가하고, 응답에 담당자별 결과 `managerResults`와 `managerSummary`를 더합니다. 담당자 추가가 실패해도 거래처 생성은 유지됩니다. `managers`를 보내지 않으면 응답은 기존과 같습니다."}]},{"revision":"2026-10-13.1","releasedAt":"2026-10-13","changes":[{"id":"tax-invoice-issue-negative-amount-allowed","type":"fix","effectiveAt":"2026-10-13","endpointKeys":["tax-invoices-issue"],"summary":"세금계산서 정발행이 음수 금액(`supplyValue`·`taxAmount`·`totalAmount`, 결제수단 금액, `tradeItems[]`의 수량·단가·공급가액·세액)을 받습니다. 이전에는 0 미만 값을 400(Invalid request body)으로 거절해 마이너스 세금계산서를 발행할 수 없었습니다."}]},{"revision":"2026-10-06.1","releasedAt":"2026-10-06","changes":[{"id":"ticket-inclusion-update-added","type":"additive","effectiveAt":"2026-10-06","endpointKeys":["tickets-update"],"summary":"티켓 수정에 `IS_INCLUDED`+`items[].isIncluded`(포함 여부)를 추가합니다. 이중계상·부가세 납부처럼 집계에서 빼야 하는 티켓을 `isIncluded=false`로 제외하고 `true`로 다시 포함할 수 있습니다."},{"id":"ticket-status-filters-added","type":"additive","effectiveAt":"2026-10-06","endpointKeys":["tickets"],"summary":"티켓 조회에 `isIncluded`(포함 여부)·`isClassified`(계정과목 지정 여부) 조건을 추가합니다. `isClassified=false`로 미분류 티켓만 받을 수 있습니다."},{"id":"ticket-update-source-exposed","type":"additive","effectiveAt":"2026-10-06","endpointKeys":["tickets","tickets-detail"],"summary":"티켓 응답에 `expenseCategoryUpdateSource`·`isIncludedUpdateSource`(값을 정한 주체)를 내보냅니다. `USER`는 사람이 확정한 값이고 그 밖은 자동 분류입니다."},{"id":"asset-scrap-history-exposed","type":"additive","effectiveAt":"2026-10-06","endpointKeys":["assets"],"summary":"자산 응답에 `scrapHistory`(`lastRequestedAt`·`lastCompletedAt`·`lastFailedAt`)를 추가해 자산별 마지막 수집 요청·성공·실패 시각을 확인할 수 있게 합니다. 수집 이력이 없으면 `null`입니다."}]},{"revision":"2026-10-04.1","releasedAt":"2026-10-04","changes":[{"id":"ticket-tags-api-added","type":"new","effectiveAt":"2026-10-04","endpointKeys":["ticket-tags","ticket-tags-create","ticket-tags-update","ticket-tag-groups"],"summary":"태그(다중 태그, v2) 조회·생성·수정과 태그 그룹 조회 API를 추가합니다. 2026-10-01부터 그랜터는 한 티켓에 태그를 여러 개 붙이는 다중 태그만 사용하며, 단계(1차·2차·…)는 태그 이름의 `/`로 표현합니다(예전 1차/2차 태그는 `1차/2차` 이름으로 이관)."},{"id":"ticket-tags-assignment-added","type":"additive","effectiveAt":"2026-10-04","endpointKeys":["tickets","tickets-detail","tickets-update","manual-transactions-create"],"summary":"티켓 조회·단건 조회 응답의 `ticketTags`(id·name·ticketTagGroup)를 공개 계약으로 문서화하고, 티켓 조회에 `ticketTagIds` 조건, 티켓 수정에 `TICKET_TAGS`+`ticketTagIds`(태그 전체 교체, 빈 배열은 모두 해제), 수기거래 생성에 `ticketTagIds`를 추가합니다."},{"id":"legacy-tag-assignment-rejected","type":"breaking","effectiveAt":"2026-10-03","endpointKeys":["tickets-update","manual-transactions-create"],"summary":"1차/2차 태그 지정 — 티켓 수정의 `TAG`·`TAG_DETAIL`(`tagId`·`tagDetailId`), 수기거래 생성의 `tagId`·`tagDetailId` — 은 2026-10-03부터 거절됩니다(400). 같은 요청의 다른 변경도 함께 반영되지 않습니다.","migration":"`ticket-tags` API로 태그 ID를 조회한 뒤, 티켓 수정은 `updateFields`에 `TICKET_TAGS`와 `items[].ticketTagIds`를, 수기거래 생성은 `manualTransactions[].ticketTagIds`를 보내세요. 예전 1차/2차 태그는 `1차/2차` 이름의 태그로 이관돼 있어 이름으로 찾을 수 있습니다."},{"id":"legacy-tag-api-deprecated","type":"deprecated","effectiveAt":"2026-10-04","endpointKeys":["tags","tags-create","tags-update","tag-details","tag-details-create","tag-details-update","tickets"],"summary":"1차/2차 태그 API(`tags`, `tags-create`, `tags-update`, `tag-details`, `tag-details-create`, `tag-details-update`)와 티켓의 `tag`·`tagId`·`tagDetail`·`tagDetailId` 필드·조건을 deprecated 로 지정합니다. 이 태그는 2026-10-01 이후 앱에 보이지 않고 갱신되지 않으며, 곧 삭제될 예정입니다.","migration":"태그 기준 데이터는 `ticket-tags`·`ticket-tag-groups`로, 티켓의 태그는 `ticketTags`로 읽고, 조건은 `ticketTagIds`를 쓰세요."}]},{"revision":"2026-10-02.1","releasedAt":"2026-10-02","changes":[{"id":"ticket-list-overview-all-workspaces","type":"breaking","effectiveAt":"2026-10-02","endpointKeys":["tickets"],"summary":"티켓 조회 API의 목록 응답이 모든 워크스페이스에서 요약 기반(목록 V2)으로 바뀝니다. 2026-09-30부터 워크스페이스별로 순차 전환하던 것을 전체로 넓힌 것으로, 기존(V1) 목록 계약은 더 이상 반환하지 않습니다. 응답 원소에는 messages·attachments 원문이 없고 commentCount(댓글 수)·attachmentCount(첨부 수)·ticketReferences(연결내역)가 있으며, isConnected·connectionCount는 결재·계약 연결을 세지 않습니다.","migration":"댓글·변경 기록·첨부 원문이 필요하면 목록 원소의 id·ticketType으로 티켓 단건 조회 API(POST /api/public-docs/tickets/detail)를 호출하세요. 개수만 필요하면 commentCount·attachmentCount를, 결재·계약을 포함한 연결 대상은 ticketReferences를 쓰세요."}]},{"revision":"2026-10-01.1","releasedAt":"2026-10-01","changes":[{"id":"contracts-api-added","type":"new","effectiveAt":"2026-10-01","endpointKeys":["contracts","contracts-detail","contracts-payment-proofs-link","contracts-payment-proofs-unlink","contracts-complete","contracts-uncomplete","contracts-documents-resend"],"summary":"업무 > 계약의 계약 목록·상세 조회, 계약 입금내역 연결·해제, 완료 처리·되돌리기, 발급 문서 재전달 API를 추가합니다. 완료 처리는 앱과 같이 계약 결제 상태를 결제 완료로 기록하고, 재전달은 Idempotency-Key header가 필수입니다."}]},{"revision":"2026-09-30.1","releasedAt":"2026-09-30","changes":[{"id":"ticket-detail-added","type":"new","effectiveAt":"2026-09-30","endpointKeys":["tickets-detail"],"summary":"티켓 한 건의 댓글·변경 기록(messages), 첨부파일(attachments) 원문과 연결내역(ticketReferences)을 함께 반환하는 티켓 단건 조회 API(POST /api/public-docs/tickets/detail)를 추가합니다."},{"id":"ticket-list-overview-rollout","type":"breaking","effectiveAt":"2026-09-30","endpointKeys":["tickets"],"summary":"티켓 조회 API의 목록 응답을 요약 기반(목록 V2)으로 워크스페이스별 순차 전환합니다. 전환된 응답에는 messages·attachments 원문이 없고 commentCount(댓글 수)와 ticketReferences(연결내역)가 추가됩니다. messageCount는 commentCount와 같은 값이고 attachmentCount는 그대로 제공합니다. isConnected·connectionCount는 결재·계약 연결을 세지 않습니다. 전환 여부는 응답 원소에 ticketReferences 필드가 있는지로 구분할 수 있습니다.","migration":"댓글·변경 기록·첨부 원문이 필요하면 목록 원소의 id·ticketType으로 티켓 단건 조회 API(POST /api/public-docs/tickets/detail)를 호출하세요. 개수만 필요하면 commentCount·attachmentCount를, 결재·계약을 포함한 연결 대상은 ticketReferences를 쓰세요."},{"id":"ticket-list-v1-contract-deprecated","type":"deprecated","effectiveAt":"2026-09-30","endpointKeys":["tickets"],"summary":"티켓 조회 API의 기존(V1) 목록 계약 — 목록 원소의 messages·attachments 원문, 결재·계약 연결을 포함하던 isConnected·connectionCount 기준 — 을 deprecated 로 지정합니다. 모든 워크스페이스가 목록 V2로 전환되면 곧 삭제될 예정입니다.","migration":"지금 V1 목록을 받고 있어도 미리 옮기세요: 개수는 commentCount·attachmentCount, 연결 대상은 ticketReferences를 쓰고, 댓글·변경 기록·첨부 원문은 티켓 단건 조회 API(POST /api/public-docs/tickets/detail)로 받으세요."}]},{"revision":"2026-09-16.1","releasedAt":"2026-09-16","changes":[{"id":"woori-personal-withdraw-account-credential-added","type":"behavioral","effectiveAt":"2026-09-16","endpointKeys":["organizations-login-connect","organizations-login-reconnect"],"summary":"우리은행(WOORI_BANK) 개인(PERSONAL) ID/PW 연동·재연동에 출금계좌번호(withdrawAccountNo)와 출금계좌 비밀번호(withdrawAccountPassword)를 추가합니다. 우리은행 개인 사이트 개편으로 두 값이 없으면 스크래핑이 실패하며, 해당 요청에서는 두 필드가 모두 필수입니다.","migration":"우리은행 개인 연동·재연동 요청에 withdrawAccountNo·withdrawAccountPassword를 함께 보내세요. 은행 사이트 계좌인증 목록에 있는 계좌만 사용할 수 있습니다. 다른 기관·우리은행 법인 요청에는 두 필드를 보내지 마세요(400)."}]},{"revision":"2026-08-14.1","releasedAt":"2026-08-14","changes":[{"id":"bank-ticket-contact-assignment-added","type":"additive","effectiveAt":"2026-08-14","endpointKeys":["tickets-update"],"summary":"계좌 티켓의 거래처를 지정하거나 재지정할 수 있도록 bulk-update-individual에 CONTACT와 contactId를 추가합니다."},{"id":"settled-without-reference-added","type":"additive","effectiveAt":"2026-08-14","endpointKeys":["tickets"],"summary":"세금계산서 티켓 응답에 실제 티켓 연결 없이 완료 처리된 상태를 나타내는 settledWithoutReference를 추가합니다."}]},{"revision":"2026-08-05.1","releasedAt":"2026-08-05","changes":[{"id":"ticket-date-window-expanded","type":"additive","effectiveAt":"2026-08-05","endpointKeys":["tickets"],"summary":"tickets 조회가 31일 초과 범위를 월 단위로 안전하게 분할·병합하며, 한 요청에서 최대 366일까지 지원합니다."},{"id":"ticket-references-bulk-create-added","type":"new","effectiveAt":"2026-08-05","endpointKeys":["ticket-references-create-bulk"],"summary":"한 요청에서 최대 25개의 티켓 연결을 생성하고 항목별 성공·실패 결과를 반환하는 bulk API를 추가합니다."},{"id":"tax-invoice-complete-without-reference-added","type":"new","effectiveAt":"2026-08-05","endpointKeys":["tickets-complete-without-reference","tickets-uncomplete-without-reference"],"summary":"미연결 세금계산서를 일괄 완료 처리하거나 완료 전 상태로 되돌리는 API를 추가합니다. 연결된 티켓은 안전하게 거부합니다."},{"id":"validation-error-details-preserved","type":"additive","effectiveAt":"2026-08-05","endpointKeys":"all","summary":"backend가 제공한 details, errors, fieldErrors 검증 상세를 Public API 오류 응답의 details로 보존합니다."}]},{"revision":"2026-07-29.1","releasedAt":"2026-07-29","changes":[{"id":"contract-change-notices-added","type":"additive","effectiveAt":"2026-07-29","endpointKeys":"all","summary":"모든 인증 Public API 응답에 현재 계약 리비전, 관련 변경 ID, 변경 로그 링크를 응답 헤더로 제공합니다."},{"id":"rate-limit-status-corrected","type":"behavioral","effectiveAt":"2026-07-29","endpointKeys":"all","summary":"Public API 앞단 또는 인증 확인 단계의 속도 제한을 401이 아닌 429로 분류하고 Retry-After를 제공합니다.","migration":"401은 API key 오류로만 처리하고, 429는 Retry-After 이후 동일 요청을 재시도하도록 클라이언트 분기를 수정하세요."},{"id":"auth-outage-status-corrected","type":"behavioral","effectiveAt":"2026-07-29","endpointKeys":"all","summary":"API key 인증 서버의 일시적 장애를 잘못된 key와 구분하여 401이 아닌 503으로 응답합니다.","migration":"401은 API key를 점검하고, 503은 일시적 장애로 분류하여 지수 백오프로 재시도하세요."},{"id":"nickname-canonicalized","type":"breaking","effectiveAt":"2026-07-29","endpointKeys":"all","summary":"응답에 함께 존재하던 nickName과 nickname을 표준 필드 nickname 하나로 통일합니다.","migration":"응답 모델과 필드 접근을 nickname으로 통일하세요."},{"id":"ticket-connection-summary-added","type":"additive","effectiveAt":"2026-07-29","endpointKeys":["tickets"],"summary":"티켓 응답에 isConnected와 connectionCount 연결 요약을 제공합니다."},{"id":"manual-transactions-create-added","type":"new","effectiveAt":"2026-07-29","endpointKeys":["manual-transactions-create"],"summary":"거래처 ID를 포함한 수기관리 전표 다건 생성 API와 안정적인 처리 확인 응답을 추가합니다. upstream이 생성 티켓을 반환하지 않으면 ticketIds와 tickets는 빈 배열입니다.","migration":"생성 성공 여부는 ok와 requestedCount로 확인하고, ticketIds가 항상 채워진다고 가정하지 마세요."},{"id":"manual-transactions-delete-added","type":"new","effectiveAt":"2026-07-29","endpointKeys":["manual-transactions-delete"],"summary":"수기관리 티켓 ID를 내부 수기거래 ID로 안전하게 변환하고 연결 상태를 확인한 뒤 삭제하는 API를 추가합니다."}]}]}