OutlinePay Docs문서 openapi.json
Changelog변경 사항

What's new in the APIAPI의 새로운 소식

Every change your integration can see, newest first: the /v1 surface, webhook events, outlinepay.js, the Developers hub, and these docs. Internal changes are not listed. An entry that costs you something carries a label. Breaking means code that was correct stops working. Stricter means input we never documented is now refused, which leaves correct code untouched. Both are defined under Stability.통합에 영향을 주는 모든 변경을 최신순으로 정리했습니다. /v1 API, 웹훅 이벤트, outlinepay.js, 개발자 허브, 그리고 이 문서가 대상입니다. 내부 변경은 싣지 않습니다. 영향이 있는 항목에는 라벨이 붙습니다. Breaking은 정상적으로 작성된 코드가 동작을 멈추는 변경이고, Stricter는 문서에 없던 입력이 이제는 거부되는 변경으로 정상적인 코드는 영향을 받지 않습니다. 두 라벨의 정의는 안정성에 있습니다.

payments docs

Post-payment invoices on payment links결제 링크의 결제 후 인보이스

Create a payment link with invoice.enabled and every payment it takes asks the processor for an invoice once it succeeds, exactly as invoice.enabled does on a single payment. The option is fixed for the life of the link, like its rail and methods, and the link echoes it back as invoice.invoice.enabled로 결제 링크를 생성하면 이 링크로 받는 모든 결제가 성공 시 처리사에 인보이스를 요청합니다. 단일 결제의 invoice.enabled와 동일하게 동작합니다. 이 옵션은 결제 레일이나 결제 수단처럼 링크가 존재하는 동안 고정되며, 링크 객체의 invoice로 그대로 반환됩니다.

Each payment the link mints then carries invoice_url and fees.invoice with the same rules as before: the invoice appears when the payment succeeds, the fee is live mode only, and receipt_url stays free.이 링크로 생성된 각 결제에는 기존과 같은 규칙으로 invoice_urlfees.invoice가 포함됩니다. 인보이스는 결제가 성공하면 나타나고, 수수료는 라이브 모드에서만 제공되며, receipt_url은 계속 무료입니다.

Asking for one when you create the link, POST /v1/payment_links:링크 생성 시 인보이스를 요청하는 경우, POST /v1/payment_links:

JSON
{
  "merchant_id": "mrc_9f2c8bAaZ1kQ",
  "amount": 4900,
  "description": "Consulting session",
  "invoice": { "enabled": true }
}
payments docs

Post-payment invoices on the Payment objectPayment 객체의 결제 후 인보이스

Create a payment with invoice.enabled and the payment processor issues an invoice once it succeeds. The invoice is at invoice_url on the payment, viewable in a browser and downloadable as a PDF. It is issued under the merchant's business details and reflects refunds.invoice.enabled로 결제를 생성하면 결제가 성공한 뒤 결제 처리사가 인보이스를 발행합니다. 인보이스는 결제의 invoice_url에 있으며, 브라우저에서 조회하거나 PDF로 저장할 수 있습니다. 가맹점 명의로 발행되고 환불 내역이 반영됩니다.

It is off by default, because each invoice costs the merchant a processor fee, and it can only be requested when the payment is created, not afterwards. receipt_url is unchanged and still free on every succeeded payment.기본값은 꺼짐입니다. 인보이스 건당 처리사 수수료가 가맹점에 부과되기 때문이며, 결제를 생성하는 시점에만 요청할 수 있고 이후에는 생성할 수 없습니다. receipt_url은 변경 없이 성공한 모든 결제에서 계속 무료로 제공됩니다.

The invoice fee appears as fees.invoice and is included in fees.total and merchant_net. It arrives about a day after the payment, on a payment.updated event, and it is live mode only: fees.invoice stays null in test mode.인보이스 수수료는 fees.invoice로 제공되며 fees.totalmerchant_net에 포함됩니다. 결제 시점보다 하루 정도 뒤에 payment.updated 이벤트로 도착하며, 라이브 모드에서만 제공됩니다. 테스트 모드에서는 fees.invoice가 null로 남습니다.

Asking for one when you create the payment:결제 생성 시 인보이스를 요청하는 경우:

JSON
{
  "merchant_id": "mrc_9f2c8bAaZ1kQ",
  "amount": 25000,
  "success_url": "https://example.com/done",
  "cancel_url": "https://example.com/cancel",
  "invoice": { "enabled": true }
}

Once it has succeeded:결제가 성공한 뒤:

JSON
{
  "id": "pay_3Nk8Qh2Lmp7T",
  "status": "succeeded",
  "receipt_url": "https://checkout.outlinepay.com/r/pay_3Nk8Qh2Lmp7T",
  "invoice_url": "https://checkout.outlinepay.com/i/pay_3Nk8Qh2Lmp7T",
  "fees": {
    "processor": 755,
    "outline": 125,
    "partner": 250,
    "dispute": 0,
    "invoice": 100,
    "total": 1230
  }
}
payments docs Breaking

Error responses list every problem in details오류 응답이 details 배열로 모든 문제를 반환합니다

The error envelope is now two keys: code classifies the refusal and details describes it, one entry per problem found. The top-level message, param, processor_message and doc_url fields moved inside the entries unchanged. Error codes, HTTP statuses and message texts are all as before. Errors has the full rule.오류 봉투가 두 개의 키로 바뀌었습니다. code는 거부의 종류를 나타내고, details는 발견된 문제를 항목 하나당 하나씩 담습니다. 최상위에 있던 message, param, processor_message, doc_url 필드는 그대로 각 항목 안으로 이동했습니다. 오류 코드, HTTP 상태 코드, 메시지 문구는 모두 이전과 같습니다. 전체 규칙은 오류 섹션에 있습니다.

details always has at least one entry, on every error the API returns. It has more than one only on write-time validation, which now reports every field it refused in one response instead of the first per call: unknown fields first, then value checks, in the reference's field order.details에는 API가 반환하는 모든 오류에서 항상 1건 이상의 항목이 있습니다. 2건 이상이 되는 경우는 저장 시점 검증뿐이며, 이제 호출마다 첫 번째 문제만 알리는 대신 거부된 모든 필드를 한 번에 반환합니다. 문서에 없는 필드가 먼저, 값 검사가 그 뒤에, 참조 문서의 필드 순서대로 정렬됩니다.

To migrate, read the entries instead of the top-level fields. An entry has the same fields the top level used to carry, so code shaped as error.details ?? [error] works on both sides of this change.마이그레이션은 최상위 필드 대신 항목을 읽도록 바꾸시면 됩니다. 항목의 필드 구성이 기존 최상위 필드와 같으므로, error.details ?? [error] 형태의 코드는 이 변경 전후 모두 동작합니다.

One bad field:하나의 필드가 잘못된 경우:

JSON
{
  "error": {
    "code": "invalid_request",
    "details": [
      {
        "param": "business.url",
        "message": "business.url must be a web URL."
      }
    ]
  }
}

Several, answered together:여러 필드가 함께 반환되는 경우:

JSON
{
  "error": {
    "code": "invalid_request",
    "details": [
      {
        "param": "business.phone",
        "message": "business.phone must be a phone number."
      },
      {
        "param": "business.url",
        "message": "business.url must be a web URL."
      }
    ]
  }
}
payments

An array request body now returns invalid_request배열 요청 본문은 이제 invalid_request로 응답합니다

A request body that is a JSON array, not an object, now returns 400 invalid_request saying the body must be a JSON object. It used to be refused as Received unknown parameter: _json.JSON 객체가 아니라 배열인 요청 본문은 이제 본문이 JSON 객체여야 한다는 메시지와 함께 400 invalid_request로 응답합니다. 이전에는 Received unknown parameter: _json으로 거부되었습니다.

docs

Countries open on request, without a US entity미국 법인 없이 요청 시 지원되는 국가

The country page now lists 42 countries whose merchants can onboard with their local business entity. We enable a country when a partner asks. A merchant onboarded this way pays its country's processing rates, and US-issued cards are priced as international cards.국가 안내 페이지에 현지 법인 그대로 온보딩할 수 있는 42개국을 정리했습니다. 파트너가 요청하시면 해당 국가를 활성화합니다. 이 방식으로 온보딩한 가맹점에는 해당 국가의 처리 수수료율이 적용되며, 미국 발급 카드는 해외카드로 분류됩니다.

sdk docs

Outstanding onboarding requirements on the Merchant objectMerchant 객체의 미완료 온보딩 요구사항

The Merchant object's processors[] now carries a nullable requirements object: the items the processor still needs before the merchant can charge, whether each blocks charges or payouts, and whether the merchant or the processor acts next.Merchant 객체processors[]requirements 객체(null 가능)가 추가되었습니다. 가맹점이 결제를 시작하기 전에 결제 처리사가 아직 필요로 하는 항목, 각 항목이 결제와 정산 중 무엇을 막는지, 다음 조치의 주체가 가맹점인지 결제 처리사인지를 담습니다.

It is null once nothing is outstanding. Changes arrive on merchant.updated.남은 항목이 없으면 null이 됩니다. 변경은 merchant.updated 이벤트로 전달됩니다.

payments

Hosted receipts on the Payment objectPayment 객체의 호스팅 영수증

The Payment object now carries a nullable receipt_url: a hosted receipt the customer can view in a browser and download as a PDF. It is set when the payment succeeds and is present on the payment.succeeded event. Payments that succeeded before this change also have one.Payment 객체receipt_url 필드(null 가능)가 추가되었습니다. 고객이 브라우저에서 조회하고 PDF로 저장할 수 있는 호스팅 영수증입니다. 결제가 성공하면 설정되며 payment.succeeded 이벤트에 포함됩니다. 이 변경 이전에 성공한 결제에도 제공됩니다.

The URL never expires, and the receipt reflects full and partial refunds.이 URL은 만료되지 않으며, 영수증에는 전액 및 부분 환불이 반영됩니다.

docs

Supported merchant category codes지원되는 업종 분류 코드(MCC) 목록

The docs now carry the full list of merchant category codes at MCCs: every code we accept as business.industry_code, the processor that accepts each, and the date we last checked the list against that processor. The industry_code reference links there now, instead of the processor's own page.문서에 업종 분류 코드 전체 목록 페이지 MCC 목록이 추가되었습니다. business.industry_code로 받는 모든 코드와 각 코드를 지원하는 처리사, 그리고 목록을 처리사와 마지막으로 대조한 날짜를 담고 있습니다. industry_code 레퍼런스도 이제 처리사 페이지 대신 이 페이지로 연결됩니다.

sdk docs Breaking

A rejected representative no longer discards the opened account대표자가 거부되어도 이미 열린 계정은 더 이상 사라지지 않습니다

When the processor refused a company merchant's representative, the 400 used to leave no record of the account that had already opened, and each retry opened another. The account is now attached the moment it opens: the 400 still answers, the merchant keeps the processor, and sending the same Attach a processor request again with a corrected representative finishes on that account instead of opening a second one. The retry reads only the representative.지금까지는 처리사가 법인 가맹점의 대표자를 거부하면 400만 남고 이미 열린 계정의 기록이 남지 않아, 재시도할 때마다 계정이 하나씩 더 열렸습니다. 이제 계정은 열리는 순간 가맹점에 연결됩니다. 400은 그대로 응답하지만 가맹점은 처리사를 유지하며, 수정한 대표자와 함께 Attach a processor 요청을 다시 보내면 두 번째 계정을 여는 대신 그 계정에서 마무리됩니다. 재시도에서는 representative만 읽습니다.

The merchant's processors[] entries gain representative_prefilled: true once the representative supplied at attach reached the processor, false when none was supplied or it was rejected. The 409 message from this endpoint now says why.가맹점processors[] 항목에 representative_prefilled가 추가됩니다. 연결 시 제공한 대표자가 처리사에 전달되면 true, 제공하지 않았거나 거부된 경우 false입니다. 이 엔드포인트의 409 메시지에는 이제 이유가 담깁니다.

Attaching an already-attached merchant was always a 409. It still is once the representative has reached the processor or onboarding is complete. A company merchant attached after this change whose representative has not reached the processor accepts one on a second POST and answers 200 with the merchant. Accounts attached before this change keep the unconditional 409.이미 연결된 가맹점에 다시 연결을 요청하면 언제나 409 였습니다. 대표자가 처리사에 전달되었거나 온보딩이 완료된 뒤에는 지금도 같습니다. 이 변경 이후에 연결된 법인 가맹점은 대표자가 아직 전달되지 않았다면 두 번째 POST로 대표자를 받으며, 200과 가맹점 객체로 응답합니다. 이 변경 이전에 연결된 계정은 기존의 무조건 409를 유지합니다.

sdk docs Breaking

Values the processor would refuse are now refused when you send them결제 처리사가 거부할 값은 이제 전송 시점에 거부됩니다

Values the payment processor is known to refuse are now refused at write time: Create a merchant, Update a merchant and Attach a processor answer 400 invalid_request naming the field in param, instead of storing the value and failing later at attach. The rules (URLs, emails, US addresses, supported MCCs, statement descriptors, tax_id, date of birth) are in Conventions · Write-time validation.결제 처리사가 거부하는 것으로 확인된 값은 이제 그 자리에서 거부됩니다. 가맹점 생성, 가맹점 수정, 처리사 연결이 값을 저장했다가 연결 시점에 실패하는 대신, param에 필드 이름을 담은 400 invalid_request로 응답합니다. 규칙(URL, 이메일, 미국 주소, 지원 MCC, 명세서 표기, tax_id, 생년월일)은 규약 · 저장 시점 검증에 있습니다.

An address now requires its country, and a business address must be in the merchant's own country. Both are the processor's own rules, previously surfaced only at attach.이제 주소에는 country가 필수이며, 사업장 주소는 가맹점 자신의 국가에 있어야 합니다. 둘 다 처리사 자체의 규칙으로, 이전에는 처리사 연결 시점에만 드러났습니다.

When the processor rejects a value on Update a merchant or Create a payment, the answer is now 400 invalid_request with the field in param and the processor's sentence in processor_message, not 502 processor_error. As on Attach a processor since 2026-08-25, a 502 now only means the processor itself failed.가맹점 수정이나 결제 생성에서 처리사가 값을 거부하면 이제 502 processor_error 대신 param에 필드 이름을, processor_message에 처리사의 설명을 담은 400 invalid_request로 응답합니다. 2026-08-25부터의 처리사 연결과 마찬가지로, 502는 이제 처리사 자체의 장애만 의미합니다.

payments Stricter

An interrupted key now enforces the same-body rule too중단된 키에도 이제 동일 본문 규칙이 적용됩니다

Reusing an Idempotency-Key with a different request body has always been 409 idempotency_conflict. Before, if the original request was cut off mid-flight (a deploy, a crash) and two minutes passed, the next request with that key executed even with a different body. It now answers the same 409. A retry with the original body still takes over and executes as before. If you hit the conflict, use a fresh key.Idempotency-Key를 다른 요청 본문과 함께 재사용하면 항상 409 idempotency_conflict로 응답해 왔습니다. 이전에는 원래 요청이 처리 중에 중단되고(배포, 프로세스 중단 등) 2분이 지나면, 그다음에 같은 키로 들어온 요청은 본문이 달라도 실행되었습니다. 이제 같은 409로 응답합니다. 원래 본문을 그대로 다시 보내는 재시도는 이전과 같이 이어받아 실행됩니다. 이 오류를 받으시면 새 키를 사용해 주십시오.

webhooks Breaking

Deleting a webhook endpoint is now permanent웹훅 엔드포인트 삭제는 이제 되돌릴 수 없습니다

Delete a webhook endpoint now returns a tombstone, {"id": ..., "object": "webhook_endpoint", "deleted": true}, the endpoint disappears from the list immediately, and its id stops resolving: a repeat DELETE answers 404 not_found. It used to return the endpoint object with status disabled and keep it in the list indefinitely. There is no undo. To deliver to the same URL again, create a new endpoint, which will have a new signing secret.웹훅 엔드포인트 삭제가 이제 {"id": ..., "object": "webhook_endpoint", "deleted": true} 형태의 삭제 확인 객체를 반환하며, 엔드포인트는 즉시 목록에서 사라지고 해당 id는 더 이상 조회되지 않습니다. 같은 id로 DELETE를 다시 호출하면 404 not_found로 응답합니다. 이전에는 상태가 disabled인 엔드포인트 객체를 반환하고 레코드를 목록에 계속 남겨 두었습니다. 되돌릴 수 없습니다. 같은 URL로 다시 전달하시려면 새 엔드포인트를 생성해 주십시오. 새 서명 시크릿이 발급됩니다.

Endpoints you deleted before this change also leave the list with this release.이 변경 이전에 삭제하신 엔드포인트도 이번 릴리스와 함께 목록에서 사라집니다.

webhooks Stricter

Active webhook endpoints are capped at 16 per mode활성 웹훅 엔드포인트가 모드당 16개로 제한됩니다

Create a webhook endpoint now refuses an endpoint past the 16th active one in a mode with 400 invalid_request. Deleting an endpoint frees its slot immediately, and existing endpoints are unaffected.웹훅 엔드포인트 생성이 이제 모드당 활성 엔드포인트 16개를 초과하면 400 invalid_request로 거부합니다. 엔드포인트를 삭제하면 그 자리는 즉시 다시 사용할 수 있으며, 기존 엔드포인트에는 영향이 없습니다.

sdk docs

Merchant-owned fields are editable until a processor is attached가맹점 소유 필드는 결제 처리사 연결 전까지 수정할 수 있습니다

Update a merchant now accepts business.product_description, business.industry_code, business.statement_descriptor and business.phone while the merchant has no processor attached. Once a processor is attached they are rejected again: from then on the merchant owns them and edits them in the account-management component.가맹점 수정이 이제 결제 처리사가 연결되지 않은 가맹점에 한해 business.product_description, business.industry_code, business.statement_descriptor, business.phone을 받습니다. 결제 처리사가 연결되면 다시 거부됩니다. 그 시점부터는 가맹점이 소유하며 계정 관리 컴포넌트에서 수정합니다.

Values are now checked before they are written, on both create and update. The processor still has the final say when you attach, and a failed attach leaves no account, so the fields stay editable.이제 생성과 수정 모두에서 저장하기 전에 값을 검사합니다. 최종 판단은 여전히 연결 시점에 결제 처리사가 하며, 연결이 실패하면 계정이 만들어지지 않으므로 필드는 계속 수정할 수 있습니다.

payments docs

A body that is not valid JSON is answered in the error envelope유효한 JSON이 아닌 본문도 표준 오류 응답 형식으로 응답합니다

A request body that is not valid JSON is now answered in the standard error envelope as 400 invalid_request, with the parser's position in the message. It used to fall through to an unstructured error page.유효한 JSON이 아닌 요청 본문은 이제 표준 오류 응답 형식400 invalid_request로 응답하며, 메시지에 파서가 실패한 위치가 담깁니다. 이전에는 구조화되지 않은 오류 페이지가 반환되었습니다.

sdk docs Breaking

A value the processor refuses is now a 400, not a 502처리사가 값을 거부하면 이제 502가 아니라 400으로 응답합니다

When the processor refuses a value on Attach a processor (a phone it cannot parse, for example), the answer is now 400 invalid_request, with the field in param (in the API's spelling, e.g. representative.phone) and the processor's own sentence in processor_message. It used to be 502 processor_error. A 502 now only means the processor itself failed.처리사 연결에서 처리사가 값을 거부하면(예: 해석할 수 없는 전화번호) 이제 400 invalid_request로 응답합니다. param에는 API 표기 그대로의 필드 이름(예: representative.phone)이, processor_message에는 처리사 자신의 설명이 담깁니다. 이전에는 502 processor_error로 응답했습니다. 이제 502는 처리사 자체의 장애만 의미합니다.

A phone number the processor cannot parse is refused at write time: representative.phone on Attach a processor and business.phone on Create a merchant return 400 invalid_request naming the field. Use E.164: '+', the country code, then the subscriber number, e.g. +12125550123 or +821012345678. National formats the processor can parse still pass.처리사가 해석할 수 없는 전화번호는 저장 시점에 거부됩니다. 처리사 연결의 representative.phone가맹점 생성business.phone이 해당 필드를 지목한 400 invalid_request를 반환합니다. E.164 형식('+', 국가 코드, 가입자 번호 순)을 사용해 주십시오. 예: +12125550123, +821012345678. 처리사가 해석할 수 있는 국가별 형식은 계속 허용됩니다.

sdk docs Breaking

Account sessions require components계정 세션의 components 필수화

Create an account session now requires components. A request without it used to mint a session covering every component. It is now refused with a 400 invalid_request naming components. Name the component(s) the page mounts, for example ["onboarding"].계정 세션 생성components가 필수가 되었습니다. 이전에는 생략하면 모든 컴포넌트를 허용하는 세션이 생성되었지만, 이제는 components를 지목한 400 invalid_request로 거부됩니다. 페이지가 마운트하는 컴포넌트만 지정해 주십시오. 예: ["onboarding"].

A session grants exactly the components you name. The quickstart shows how to scope a session to a page.세션은 지정하신 컴포넌트만 정확히 허용합니다. 페이지 단위의 범위 지정 방법은 시작하기에서 확인하실 수 있습니다.

webhooks docs

How to consume webhook events웹훅 이벤트 처리 방법

The Events & webhooks conventions now spell out the consumption model. Every event is self-sufficient, so two events about the same object can arrive out of order and each is still safe to act on. Skip duplicates by event id, and when you need an object's latest state, retrieve the object.이벤트와 웹훅 규약에 이벤트 처리 모델을 정리했습니다. 모든 이벤트는 그 자체로 완결적이므로, 같은 객체에 대한 두 이벤트가 순서가 뒤바뀌어 도착해도 각각 그대로 처리하셔도 안전합니다. 이벤트 id로 중복을 걸러 주시고, 객체의 최신 상태가 필요하시면 객체를 조회해 주십시오.

Two smaller additions: List events is documented as newest first in emission order, and an event you received can be fetched again by id with Retrieve an event.작은 추가 두 가지입니다. 이벤트 목록 조회가 발행 순서 기준 최신순임을 명시했고, 수신하신 이벤트는 이벤트 조회로 id를 통해 다시 조회하실 수 있습니다.

docs

The API reference, reorganizedAPI 레퍼런스 개편

Conventions are grouped into four sections (Requests, Objects & fields, Money movement, Events & webhooks) and the side nav lists each one. Each endpoint links the conventions that apply to it.규약을 네 개의 묶음(요청, 객체와 필드, 자금 흐름, 이벤트와 웹훅)으로 나누고, 사이드 내비게이션에 각각을 표시했습니다. 각 엔드포인트는 해당 호출에 적용되는 규약으로 연결됩니다.

The nav has a filter box that matches English and Korean, and code samples now default to PHP. Picking a language tab applies to every sample on the page and is remembered.내비게이션에 영어와 한국어를 모두 인식하는 필터 입력이 있고, 코드 예시는 이제 PHP가 기본입니다. 언어 탭을 선택하시면 페이지의 모든 예시에 적용되며 선택이 기억됩니다.

dashboard sdk

The component guide now lives in the example merchant dashboard컴포넌트 가이드가 이제 예시 가맹점 대시보드로 통합되었습니다

The per-surface compliance pages in the Developers hub are gone, and their milestone steps now link into the example merchant dashboard, which mounts every component through OutlinePay.mount the same way your page does.개발자 허브의 표면별 컴플라이언스 페이지가 없어지고, 해당 마일스톤 단계들은 이제 예시 가맹점 대시보드로 연결됩니다. 이 대시보드는 귀사의 페이지와 똑같이 모든 컴포넌트를 OutlinePay.mount로 마운트합니다.

The example dashboard uses outlinepay.js, a wrapper around Connect.js that adds clearer error messages and injects the OutlinePay merchant terms before the processor's KYC.예시 대시보드는 outlinepay.js를 사용합니다. Connect.js를 감싼 래퍼로, 더 명확한 오류 메시지를 제공하고 프로세서 KYC 이전에 OutlinePay 가맹점 약관을 삽입합니다.

dashboard

The acting-as merchant now shows on the milestones home선택된 가맹점이 이제 마일스톤 홈에 표시됩니다

The milestones home now shows the acting-as bar, so you can switch which example merchant its steps open. Checkout, payment links, and the example dashboard act as that merchant. Creating a merchant or attaching a processor stays partner-level.마일스톤 홈에 이제 선택된 가맹점 표시줄이 나타나, 컴포넌트 단계가 어느 예시 가맹점으로 열리는지 전환할 수 있습니다. 결제, 결제 링크, 예시 대시보드는 선택된 가맹점으로 실행됩니다. 가맹점 생성이나 프로세서 연결은 파트너 단위로 유지됩니다.

payments Breaking

Idempotency keys are kept for 24 hours, and a failed attempt no longer blocks retries멱등성 키 보관 기간이 24시간으로 바뀌고, 실패한 시도가 재시도를 막지 않습니다

Idempotency keys are now kept for 24 hours rather than 30 days. Within the window a retry replays the stored response exactly as before. After it, reusing the string starts a new request. Retries usually arrive within minutes, so this only affects a workflow that replays a key days later. Idempotency in the conventions now says 24 hours.멱등성 키 보관 기간이 30일에서 24시간으로 변경되었습니다. 기간 내 재시도는 이전과 같이 저장된 응답을 그대로 반환하고, 기간이 지난 뒤 같은 문자열을 다시 사용하면 새 요청으로 처리됩니다. 재시도는 보통 수 분 안에 도착하므로, 며칠 뒤에 키를 재사용하는 경우가 아니라면 영향이 없습니다. 컨벤션의 멱등성 항목도 24시간으로 갱신되었습니다.

A key whose first attempt ended in a raised error, for example the 400 for an unknown payment_id on Create a refund, used to answer every later retry with 409 idempotency_conflict until the key was pruned. Now the key stays usable after an error and the retry executes fresh, as retries after a 5xx always have. A request cut off mid-flight is also taken over by the next retry after a short window.첫 시도가 오류로 끝난 키(예: 환불 생성에서 알 수 없는 payment_id로 인한 400)는 이전에는 키가 정리될 때까지 모든 재시도에 409 idempotency_conflict를 반환했습니다. 이제 오류가 나도 키는 계속 사용할 수 있으며, 재시도는 5xx 이후의 재시도와 마찬가지로 새로 실행됩니다. 처리 중 중단된 요청도 잠시 후 다음 재시도가 이어받아 처리합니다.

docs

The reference now labels 404 and 400 by where the id wasAPI 레퍼런스의 404/400 표기가 실제 동작과 일치하도록 수정되었습니다

Create a payment, List payments, Create a refund and List refunds documented 404 for an unknown merchant_id or payment_id. Those ids arrive in the body or a query filter, not the path, so the API has always answered 400 with not_found, and the reference now says so. Nothing changed at runtime.결제 생성, 결제 목록, 환불 생성, 환불 목록은 알 수 없는 merchant_idpayment_id에 대해 404로 문서화되어 있었습니다. 이 id들은 경로가 아니라 본문이나 쿼리 필터로 전달되므로 API는 항상 not_found를 담은 400으로 응답해 왔고, 이제 레퍼런스도 그렇게 안내합니다. 실제 동작은 달라진 것이 없습니다.

The endpoints that do resolve an id from the path (the six Retrieve endpoints, Update a merchant, Attach a processor, Create an account session, Deactivate a payment link, Delete a webhook endpoint) now document the 404 they actually return. The rule is under Errors: the status says where the unknown id was, 404 for the path, 400 for anything passed.경로에서 id를 읽는 엔드포인트(조회 엔드포인트 여섯 개, 가맹점 수정, 프로세서 연결, account session 생성, 결제 링크 비활성화, 웹훅 엔드포인트 삭제)는 이제 실제로 반환하는 404를 문서화합니다. 규칙은 오류에 정리되어 있습니다. 상태 코드는 알 수 없는 id가 어디에 있었는지를 나타내며, 경로면 404, 그 외에 전달된 값이면 400입니다.

payments sdk docs Breaking

The onboarding component now collects the OutlinePay merchant terms온보딩 컴포넌트가 이제 OutlinePay 가맹점 약관 동의를 받습니다

Mounting the onboarding component now shows the OutlinePay merchant terms ahead of the processor's form. The merchant agrees once, on a screen we host, and that acceptance covers every processor. outlinepay.js reads the new terms field on the account session response and handles the screen, so there is nothing to build. If your Content-Security-Policy sets frame-src, allow the origin of terms.url.온보딩 컴포넌트를 마운트하면 이제 결제 프로세서의 양식에 앞서 OutlinePay 가맹점 약관이 표시됩니다. 가맹점은 당사가 호스팅하는 화면에서 한 번만 동의하며, 이 동의는 모든 결제 프로세서에 적용됩니다. outlinepay.js가 account session 응답의 새 terms 필드를 읽어 화면을 직접 처리하므로 귀사가 구현할 것은 없습니다. Content-Security-Policy에 frame-src를 설정하셨다면 terms.url의 오리진을 허용해 주세요.

The merchant object gains a read-only terms_acceptance field, and a merchant.updated event fires on acceptance.merchant 객체에 읽기 전용 terms_acceptance 필드가 추가되었고, 동의 시점에 merchant.updated 이벤트가 발송됩니다.

For merchants created from August 16 onward, Create a payment and Create a payment link refuse with 409 merchant_terms_not_accepted until the merchant has accepted. In the normal flow this never fires, because the onboarding component collects the acceptance before the merchant can finish verification. It exists for integrations that mount the processor's SDK without outlinepay.js. Merchants created earlier are exempt. The rule is under Onboarding is required before charging.8월 16일 이후 생성된 가맹점은 약관에 동의하기 전까지 결제 생성과 결제 링크 생성이 409 merchant_terms_not_accepted로 거부됩니다. 온보딩 컴포넌트가 검증 완료 전에 동의를 받기 때문에 정상 흐름에서는 이 오류가 발생하지 않으며, outlinepay.js 없이 프로세서 SDK를 직접 마운트한 연동을 위한 것입니다. 그 이전에 생성된 가맹점은 예외입니다. 자세한 규칙은 결제 전 온보딩 필수에 정리되어 있습니다.

payments Breaking

Amounts under $1.00 are refused up front as invalid_request$1.00 미만 금액은 이제 invalid_request로 즉시 거부됩니다

An amount below 100 on Create a payment or Create a payment link now returns 400 invalid_request naming amount in param, before the processor is called. A below-minimum amount used to reach the processor and come back as 502 processor_error.결제 생성 또는 결제 링크 생성에서 amount가 100 미만이면 이제 결제 프로세서를 호출하기 전에 paramamount를 담은 400 invalid_request가 반환됩니다. 이전에는 최소 금액 미만이 프로세서까지 전달된 뒤 502 processor_error로 돌아왔습니다.

payments docs Stricter

A parameter we do not document is now refused, not ignored문서에 없는 파라미터를 이제 무시하지 않고 거부합니다

Send a parameter an endpoint does not document and it now comes back 400 invalid_request naming it in param, where it used to be accepted and quietly dropped. A misspelled filter like ?statuss=succeeded no longer returns a 200 with the wrong data.엔드포인트가 문서화하지 않은 파라미터를 보내시면 이제 param에 해당 이름을 담은 400 invalid_request가 반환됩니다. 이전에는 그대로 받아들이고 조용히 버렸습니다. ?statuss=succeeded 같은 오타 필터가 더 이상 잘못된 데이터와 함께 200으로 돌아오지 않습니다.

The refusal names the nearest parameter that endpoint does take, when there is one: Received unknown parameter: staring_after. Did you mean starting_after? Each endpoint's reference entry lists everything it accepts, and Errors states the rule.거부 응답에는 해당 엔드포인트가 받는 파라미터 중 가장 가까운 이름이 함께 담깁니다. 예를 들어 Received unknown parameter: staring_after. Did you mean starting_after?와 같습니다. 각 엔드포인트가 받는 전체 목록은 레퍼런스 항목에 있으며, 규칙은 오류에 정리되어 있습니다.

Only top-level names are checked. Keys inside a documented object and metadata are not.검사 대상은 최상위 이름뿐입니다. 문서화된 객체 안의 키와 metadata는 검사하지 않습니다.

docs

Stability policy, and two labels instead of one안정성 정책, 그리고 하나가 아닌 두 개의 라벨

The reference has a new Stability section: what we add without warning, what counts as a breaking change, and what your integration has to tolerate. /v1 may still change until live keys are issued and is frozen after that.레퍼런스에 안정성 항목이 추가되었습니다. 예고 없이 추가하는 변경, 호환성이 깨지는 변경의 기준, 귀사의 연동이 견뎌야 하는 것을 정리합니다. /v1은 라이브 키 발급 전까지 바뀔 수 있으며, 그 이후에는 고정됩니다.

Entries now carry one of two labels rather than a single Breaking badge. Breaking is a documented request whose answer we changed. Stricter is input we never documented and no longer accept, which does not affect correct code.각 항목에는 이제 Breaking 하나가 아니라 두 가지 라벨 중 하나가 붙습니다. Breaking은 문서에 있던 요청의 응답이 바뀐 변경입니다. Stricter는 문서에 없던 입력을 더 이상 받지 않는 변경으로, 정상적으로 작성된 코드에는 영향이 없습니다.

One addition to Amounts, timestamps & ids: the length and prefix of an id can change. Store ids as text and do not parse them.금액 · 시각 · 식별자에 한 가지를 덧붙였습니다. ID의 길이와 접두사는 바뀔 수 있습니다. ID는 문자열로 저장하시고 파싱하지 마십시오.

webhooks docs

The webhook signature timestamp is the delivery time웹훅 서명 타임스탬프는 전송 시각입니다

The t in a webhook signature is when that delivery attempt was sent, not when the event was created. Every retry is signed fresh, so a delivery that arrives an hour after the event still verifies inside a five-minute window. The docs used to say to reject anything older than five minutes, which read as event age and would drop retries. Webhook delivery and the Quickstart's verification sample now say so. The signature itself is unchanged.웹훅 서명의 t는 해당 전송 시도를 보낸 시각이며, 이벤트가 생성된 시각이 아닙니다. 재시도할 때마다 새로 서명하므로, 이벤트 발생 한 시간 뒤에 도착한 전송도 5분 허용 범위 안에서 정상적으로 검증됩니다. 5분이 지난 것은 거부하라는 기존 문구는 이벤트의 경과 시간으로 읽혀 재시도를 버리게 할 수 있었습니다. 웹훅 전달 항목과 퀵스타트의 검증 예제에 이를 명시했습니다. 서명 자체는 바뀌지 않았습니다.

docs dashboard

Support contact지원 문의처

Every docs page and the Developers hub now carry [email protected]. We answer within one business day. Security reports go to [email protected].이제 모든 문서 페이지와 개발자 허브에 [email protected] 주소가 표시됩니다. 영업일 기준 하루 안에 답변드립니다. 보안 관련 제보는 [email protected]으로 보내주십시오.

dashboard

A shorter nav in the Developers hub개발자 허브 내비게이션 정리

The hub now navigates by Milestones, Docs, and Settings. API keys, Sites, and Team moved under Settings. Payments, merchants, and webhook endpoints are surfaces the milestones walk you through, so they lost their standalone tabs. Every page kept its URL, so a bookmark still works.개발자 허브의 내비게이션이 Milestones, Docs, Settings로 정리되었습니다. API 키, Sites, Team은 Settings 아래로 옮겼습니다. 결제, 가맹점, 웹훅 엔드포인트는 마일스톤을 따라가면서 다루는 화면이라 개별 탭을 없앴습니다. 각 페이지의 URL은 그대로이므로 저장해 두신 링크는 계속 동작합니다.

dashboard

The partner dashboard asks an admin to accept the agreement파트너 대시보드에서 관리자가 계약서에 동의합니다

Opening the partner dashboard now asks an admin to accept the Partner Agreement before continuing. Acceptance is once per company and recorded with the version accepted, so other admins are not asked again.이제 파트너 대시보드에 처음 들어가시면 관리자가 파트너 계약서에 동의한 뒤 진행하게 됩니다. 동의는 회사당 한 번이며 동의한 버전과 함께 기록되므로 다른 관리자에게 다시 묻지 않습니다.

The Developers hub is not gated, keys keep working, and no /v1 call answers differently while a company has yet to accept.개발자 허브는 막히지 않고, 키도 그대로 동작하며, 동의 전이라도 /v1 응답은 달라지지 않습니다.

payments docs Breaking

A list endpoint names the parameter it could not accept목록 엔드포인트가 받을 수 없었던 파라미터를 지목합니다

A starting_after that names nothing now answers like any other unknown id: 400 carrying not_found, the cursor quoted back, and starting_after in param. It used to be a bare 404 not_found reading "No such resource." A cursor resolves against the filters you sent alongside it, so a request that pairs a cursor with a filter excluding it is refused the same way.아무것도 가리키지 않는 starting_after는 이제 다른 알 수 없는 id와 동일하게 응답합니다. not_found 코드를 담은 400이며, 전달하신 커서를 그대로 인용하고 paramstarting_after를 담습니다. 이전에는 "No such resource."라는 문장만 담긴 404 not_found였습니다. 커서는 함께 보내신 필터와 같이 조회되므로, 커서가 가리키는 객체를 제외하는 필터와 함께 전달하시면 동일하게 거부됩니다.

Breaking: a limit we cannot read as an integer is now 400 invalid_request with param, where limit=abc used to be read as zero and served a page of one. A limit outside 1 to 100 is still clamped rather than refused, so limit=500 returns 100.호환성 변경: 정수로 읽을 수 없는 limit은 이제 param이 담긴 400 invalid_request입니다. 이전에는 limit=abc가 0으로 읽혀 1건만 반환했습니다. 1~100을 벗어난 값은 여전히 거부하지 않고 범위 안으로 조정하므로, limit=500은 100건을 반환합니다.

Breaking: an unknown status on GET /v1/payments, or an unknown type on GET /v1/events, is now 400 invalid_request listing the values we accept, where both used to return an empty data array. The event types are the same catalog the Event object publishes.호환성 변경: GET /v1/payments의 알 수 없는 statusGET /v1/events의 알 수 없는 type은 이제 허용되는 값 목록을 함께 담은 400 invalid_request입니다. 이전에는 두 경우 모두 빈 data 배열을 반환했습니다. 이벤트 타입은 Event 객체에 공개된 목록과 동일합니다.

Docs: Pagination now says where the next cursor comes from and what a bad one does, and Errors states the rule the two share: not_found for an id that names nothing, invalid_request for a value we cannot accept, and param naming the field either way.문서: 페이지네이션에 다음 커서를 어디서 얻는지와 잘못된 커서가 어떻게 처리되는지를 명시했고, 오류에 두 경우가 공유하는 규칙을 정리했습니다. 아무것도 가리키지 않는 id는 not_found, 받을 수 없는 값은 invalid_request이며, 두 경우 모두 param에 해당 필드 이름을 담습니다.

payments docs Breaking

List refunds, and one rule for unknown ids환불 목록 조회, 그리고 알 수 없는 id에 대한 한 가지 규칙

GET /v1/refunds lists your refunds across every payment, newest first, with payment_id to narrow it to one. A payment already embeds an abbreviated list of its refunds. This endpoint returns the full Refund object.GET /v1/refunds로 모든 결제에 걸친 환불 내역을 최신순으로 조회하실 수 있으며, payment_id로 특정 결제 한 건으로 좁히실 수 있습니다. 결제 객체에는 이미 해당 결제의 환불이 간략형으로 담겨 있습니다. 이 엔드포인트는 전체 Refund 객체를 반환합니다.

Breaking: an id that names nothing now answers not_found with param and the id quoted back, wherever you sent it, and the HTTP status tells you where that was: 404 in the path, 400 as a body field or a list filter. In practice: a payment_id or merchant_id that resolves to nothing still returns 400 on creating a payment, a payment link, or a refund. Its code changes from invalid_request to not_found. Filtering those lists by such an id changes from 404 to 400.호환성 변경: 아무것도 가리키지 않는 id는 어디에 보내셨든 이제 not_found 코드와 param을 담고 해당 id를 그대로 인용해 응답하며, HTTP 상태가 그 id를 어디에 두셨는지 알려드립니다. 경로에 있으면 404, 본문 필드나 목록 필터로 전달하신 경우에는 400입니다. 따라서 결제, 결제 링크, 환불 생성에서 조회되지 않는 payment_idmerchant_id는 기존의 400을 유지합니다. 코드는 invalid_request에서 not_found로 바뀝니다. 각 목록 조회의 필터는 404에서 400으로 바뀝니다.

Docs: Conventions now shows a dependency-free PHP snippet for generating an Idempotency-Key, the generated request samples mint a key instead of hardcoding one, and the refund endpoint links to the rule rather than only stating it.문서: Conventions에 Idempotency-Key를 생성하는 라이브러리 없는 PHP 예제가 추가되었습니다. 생성되는 요청 예제도 키를 고정값으로 넣는 대신 매번 생성하며, 환불 엔드포인트에서 해당 규칙으로 바로 이동할 수 있습니다.

payments

Card details on the Payment objectPayment 객체의 카드 정보

Card payments now report the card that settled them. A new nullable card object on the Payment object carries brand, network, funding, last4, exp_month, exp_year, country, authorization_code, and wallet.카드로 결제된 건은 이제 결제에 사용된 카드 정보를 제공합니다. Payment 객체에 새로 추가된 card 객체(null 가능)는 brand, network, funding, last4, exp_month, exp_year, country, authorization_code, wallet을 담습니다.

It arrives with the other settlement details: null on the outcome event, filled on the payment.updated event that follows, like fees.processor. It stays null on a payment no card settled.다른 정산 정보와 같은 시점에 도착합니다. 결과 이벤트에서는 null이고, 뒤이어 오는 payment.updated 이벤트에서 채워집니다 (fees.processor와 동일한 방식). 카드로 정산되지 않은 결제에서는 계속 null입니다.

dashboard

The Developers hub개발자 허브

The demo is now the Developers hub. Old /demo URLs redirect permanently, so saved links keep working.데모가 개발자 허브로 개편되었습니다. 기존 /demo URL은 영구 리디렉션되므로 저장해 두신 링크도 계속 동작합니다.

New in the hub: Sites registers your own pages so processor emails send merchants to your site, and test-mode payments can simulate a bank-transfer deposit.허브에 새로 추가된 기능: Sites에 귀사 페이지를 등록하면 결제 처리사가 보내는 이메일이 가맹점을 귀사 사이트로 안내합니다. 또한 테스트 모드 결제에서 계좌 이체 입금을 시뮬레이션할 수 있습니다.

The Partner Agreement is now published.파트너 계약서가 공개되었습니다.

payments

Bank transfer payment method계좌 이체 결제 수단

A new bank_transfer method: the customer pushes funds from their bank. It is distinct from bank_debit, which pulls funds with the customer's authorization.새로운 bank_transfer 결제 수단이 추가되었습니다. 고객이 은행에서 직접 돈을 보내는 방식으로, 고객의 동의를 받아 출금하는 bank_debit과는 이름은 비슷하지만 서로 다른 결제 수단입니다.

Three preconditions: the customer's email is required when it is offered, it is not available on payment links, and it is US-only for now. See the testing page to try it on a test key.세 가지 전제 조건이 있습니다. 이 수단을 제공하려면 고객 이메일이 필요하고, 결제 링크에서는 사용할 수 없으며, 현재 미국 결제에만 제공됩니다. 테스트 키로 사용해 보시려면 테스트 페이지를 참고하십시오.

dashboard webhooks

Webhook delivery health웹훅 전송 상태

The Developers hub now shows the delivery health of every webhook endpoint you have registered, so a failing receiver is visible in the dashboard rather than as missing events.이제 개발자 허브에서 등록하신 모든 웹훅 엔드포인트의 전송 상태를 확인할 수 있습니다. 수신 서버에 문제가 생기면 이벤트 누락으로 뒤늦게 발견하는 대신 대시보드에서 바로 확인됩니다.

docs

Docs upgrades문서 개선

The API reference gained a guide to Idempotency-Key under Conventions, syntax-highlighted request samples, payment lifecycle diagrams, and Korean descriptions for every schema and parameter.API 레퍼런스에 Conventions의 Idempotency-Key 사용 안내, 구문 강조된 요청 예제, 결제 상태 흐름 다이어그램이 추가되었고, 모든 스키마와 파라미터에 한국어 설명이 제공됩니다.

payments Breaking

Response shape cleanup응답 구조 정리

Four breaking changes to response shapes. customer_email is now customer.email. created is renamed created_at on every resource and on the event envelope. Every resource names its type in object. fees gains a dispute line, so total is always the exact sum of the lines.응답 구조에 호환성이 깨지는 변경 4건이 있습니다. customer_emailcustomer.email로 바뀌었습니다. created는 모든 리소스와 이벤트 봉투에서 created_at으로 이름이 바뀌었습니다. 모든 리소스는 object 필드에 자신의 타입을 표시합니다. feesdispute 항목이 추가되어 total이 항상 각 항목의 정확한 합이 됩니다.

Validation errors now name the offending field in error.param.유효성 오류는 이제 문제가 된 필드를 error.param에 명시합니다.

webhooks

Real processor fees on payment.updatedpayment.updated의 실제 처리사 수수료

A new payment.updated event delivers the processor's own fee once the processor reports it. fees.processor is null on the outcome event and filled a moment later, so record net amounts from payment.updated, not from payment.succeeded. The full rule is under Conventions.새로운 payment.updated 이벤트가 결제 처리사가 보고한 실제 수수료를 전달합니다. fees.processor는 결과 이벤트 시점에는 null이고 잠시 후 채워지므로, 정산 금액은 payment.succeeded가 아니라 payment.updated 기준으로 기록하십시오. 자세한 규칙은 Conventions를 참고하십시오.

sdk

outlinepay.jsoutlinepay.js

outlinepay.js is the supported way to embed merchant onboarding and dashboard components in the browser. It is served from js.outlinepay.com, and every failure carries an error code. The quickstart shows the full flow.outlinepay.js는 브라우저에서 가맹점 온보딩과 대시보드 컴포넌트를 삽입하는 공식 지원 방식입니다. js.outlinepay.com에서 제공되며, 모든 실패에는 오류 코드가 포함됩니다. 전체 흐름은 시작하기에서 확인하실 수 있습니다.

payments

ACH bank debitACH 계좌 출금

Payments can settle by ACH debit with the new bank_debit method, and a bank_verification request parameter chooses how the bank account is proven. Because bank rails settle later, the Payment object gains a processing status and a payment.processing event, and failed payments carry the bank's decline reason.새로운 bank_debit 수단으로 ACH 출금 결제를 받을 수 있습니다. 계좌 확인 방식은 bank_verification 요청 파라미터로 선택합니다. 은행 결제는 정산이 늦기 때문에 Payment 객체processing 상태와 payment.processing 이벤트가 추가되었고, 실패한 결제에는 은행의 거절 사유가 담깁니다.

Also new: a merchant_not_onboarded error when charging a merchant that has not finished onboarding, and account sessions can request the balances and documents components.온보딩을 마치지 않은 가맹점에 결제를 시도하면 merchant_not_onboarded 오류가 반환되고, 계정 세션에서 balances, documents 컴포넌트를 요청할 수 있습니다.

payments docs

OutlinePay API v1OutlinePay API v1

The baseline this changelog starts from: payments, payment links, refunds, a read mirror of disputes, merchants and onboarding, signed webhooks, separate test and live modes, and idempotency keys, documented in the API reference and the quickstart.이 변경 사항 페이지의 기준선입니다. 결제, 결제 링크, 환불, 분쟁 조회, 가맹점 생성과 온보딩, 서명된 웹훅, 테스트/라이브 모드 분리, 멱등성 키를 제공하며 API 레퍼런스시작하기에 문서화되어 있습니다.

Changes made before the first partner integration are not listed here.첫 파트너 통합 이전의 변경은 여기에 싣지 않습니다.