savrsoft

API 문서

고객사의 케이터링 데이터를 읽고 쓸 수 있는 심플한 REST API와 실시간 이벤트를 위한 서명된 웹훅을 제공합니다. 앱의 Developer 메뉴에서 키를 생성하세요.

인증

모든 API 요청은 API 키를 베어러 토큰으로 사용합니다. 앱의 Developer → API keys에서 키를 생성하세요. 키는 읽기 전용 또는 읽기/쓰기.

curl https://savrsoft.com/api/v1/events \
  -H "Authorization: Bearer savr_live_your_key_here"

기본 URL: https://savrsoft.com/api/v1 · 모든 응답은 JSON입니다. 오류는 { "error": "…" } 형태로 4xx/5xx 상태 코드와 함께 반환됩니다.

상태 코드

코드발생 시점
200읽기 성공.
201생성 성공. 응답 본문은 { "id": 42 } — 나머지 정보가 필요하면 해당 레코드를 조회하세요.
400필수 항목이 누락된 경우입니다. 예: { "error": "name is required" }, 또는 페이지네이션 커서를 읽을 수 없는 경우입니다.
401API 키가 없거나, 형식이 잘못되었거나, 폐기된 키입니다.
403쓰기 작업에 읽기 전용 키가 사용되었습니다. 다음 위치에서 읽기/쓰기 키를 생성하세요 개발자 → API 키.
404해당 레코드가 존재하지 않거나 다른 조직에 속해 있습니다 — 이 두 경우는 의도적으로 구분되지 않습니다. 인식되지 않는 경로나 메서드에 대해서도 동일하게 반환됩니다.

이 API의 범위

이 API는 읽기 및 생성만 가능합니다. 다음과 같은 엔드포인트는 없습니다 DELETE, PUT 또는 PATCH 엔드포인트 — 이를 사용하는 요청은 다음을 반환합니다 404 Unknown endpoint, 이는 인식되지 않는 경로와 동일합니다. 삭제와 수정은 앱에서 이루어지므로, 통합을 통해 케이터링 업체의 기록을 삭제할 수 없습니다. 생성 이상의 쓰기 권한이 필요하다면, 구축하려는 내용을 알려주세요.

페이지네이션

모든 목록 엔드포인트는 페이지네이션을 지원합니다. limit (기본값 100, 최대 500) 값을 전달하고 next_cursorhas_morefalse:

{
  "data": [ ... ],
  "has_more": true,
  "next_cursor": "eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0"
}

커서를 그대로 다시 전달하면 다음 페이지를 가져올 수 있습니다. 인코딩 방식이 변경될 수 있으므로 이를 불투명한 값으로 취급하세요:

curl "https://savrsoft.com/api/v1/clients?limit=100&cursor=eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0" \
  -H "Authorization: Bearer savr_live_your_key_here"

페이징은 오프셋이 아닌 마지막으로 받은 행을 기준으로 고정되므로, 페이지를 넘기는 동안 행이 생성되거나 삭제되어도 레코드를 건너뛰거나 중복해서 받는 일이 없습니다. 커서는 무기한 유효합니다.

매개변수동작
limit1–500. 값이 없거나 0, 음수 또는 숫자가 아니면 기본값 100으로 처리되며, 500을 초과하는 값은 500으로 제한됩니다.
cursornext_cursor 이전 페이지에서 가져온 값입니다. 첫 페이지에서는 생략하세요. 손상되었거나 다른 엔드포인트에서 발급된 읽을 수 없는 커서는 처음부터 자동으로 다시 시작하는 대신 400 Invalid cursor 를 반환합니다.

정렬 기준: /events/invoices 는 최신순; /recipes/clients 는 이름순이며, 이름이 같을 경우 id로 순서를 정해 이름이 중복되어도 페이징이 안정적으로 유지됩니다.

엔드포인트

GET/v1/me

귀하의 조직 정보(id, name, currency).

GET/v1/events

이벤트 목록을 최신순으로 조회합니다. 페이지네이션 적용 — 위의 페이지네이션 항목을 참고하세요.

GET/v1/events/{id}

요리가 포함된 이벤트 하나입니다.

POST/v1/events · 읽기/쓰기 권한 필요

이벤트를 생성합니다. Body:

{ "name": "Acme holiday party", "event_date": "2026-12-18",
  "guests": 120, "price_per_head": 55, "event_type": "corporate" }
GET/v1/recipes · GET/v1/recipes/{id}

레시피 목록(알레르기 유발 성분 및 식단 플래그 포함)을 조회하거나, 재료가 포함된 레시피 하나를 조회합니다. 목록은 이름순으로 정렬되며 페이지네이션이 적용됩니다.

GET/v1/clients · POST/v1/clients

고객 목록(이름순, 페이지네이션 적용)을 조회하거나 새 고객을 생성합니다. 생성 Body: { "name": "...", "email": "...", "phone": "...", "company": "..." }

GET/v1/invoices · GET/v1/invoices/{id}

계산된 합계가 포함된 청구서 목록을 최신순으로 조회하거나, 항목이 포함된 청구서 하나를 조회합니다. 목록에는 페이지네이션이 적용됩니다.

웹훅

엔드포인트를 다음 위치에 등록하세요: Developer → Webhooks. 이벤트가 발생하면 저희가 POST 방식으로 JSON 페이로드를 전송합니다:

{
  "id": "evt_abc123",
  "type": "event.inquiry",
  "created_at": "2026-07-17T18:20:00.000Z",
  "data": { "id": 42, "name": "Corporate lunch — Dana Ruiz", ... }
}

이벤트 유형

유형발생 시점
event.created이벤트가 생성됨
event.inquiry새로운 온라인 케이터링 요청이 도착함
invoice.sent인보이스가 발송됨으로 표시됨
invoice.paid인보이스가 결제완료로 표시됨
proposal.approved클라이언트가 제안서를 승인함
client.created클라이언트가 생성됨

서명 검증

모든 전송에는 x-savrsoft-signature 헤더가 포함됩니다: 웹훅 서명 시크릿으로 키를 생성한 원본 요청 본문의 HMAC-SHA256입니다. 동일하게 계산하여 비교하세요:

// Node.js
const crypto = require('crypto');
const sig = crypto.createHmac('sha256', SIGNING_SECRET)
  .update(rawBody).digest('hex');
if (sig !== req.headers['x-savrsoft-signature']) reject();
개인정보 처리방침 · 보안 · 이용약관 · 궁금한 점이 있으신가요? savrsoft.com · 몇 분 만에 케이터링 연동을 구축하세요.