Petnow LogoPetnow
Web Integration

Server Integration

고객사 서버에서 촬영 세션을 발급하고, 신뢰 가능한 결과를 수신하는 Server Integration API를 안내합니다.

개요

Server Integration은 고객사 서버가 API로 1회용 촬영 세션을 사용자마다 발급하고, 완료 결과를 신뢰 가능한 형태로 서버에서 수신하는 방식입니다. Server API와 동일한 호스트·인증(x-petnow-api-key)을 사용합니다.

항목
Base URLhttps://api.petify.petnow.io
인증x-petnow-api-key 헤더 (API Key 발급)
응답 형식성공: { "success": true, "data": ... } / 실패: { "errors": [{ "code": "PETNOWB2B..." }] }

전체 라운드트립

  1. 서버가 POST /v2/hosted-sessions로 세션을 발급받고, 응답의 1회용 웹 URL을 사용자에게 전달합니다.
  2. 사용자가 Petnow 촬영 페이지에서 촬영을 마치면 브라우저가 redirectUrl로 복귀합니다. 완료 시 petify_code에 1회용 결과 코드가 담깁니다.
  3. 서버가 GET /v2/hosted-results/{code}로 코드를 1회 소비하여 신뢰 가능한 결과를 받습니다.
  4. 리다이렉트나 코드를 유실했다면 언제든 GET /v2/hosted-sessions/{id}로 같은 결과를 조회할 수 있습니다.

반려동물 식별 모델

이 API에서 반려동물은 고객사 시스템의 ID(externalPetId)로만 참조됩니다. Petify 내부 ID는 노출되지 않습니다. 등록(REGISTER) 시 externalPetId가 반려동물과 연결(매핑)되고, 이후 인증·검색 결과도 이 ID로 반환됩니다.

매핑은 Web Integration의 등록을 통해서만 생성됩니다. 기존 모바일 SDK나 Server API(/v2/pets)로 등록한 반려동물에는 매핑이 없으므로 인증 대상으로 지정할 수 없고(404 PETNOWB2B21002), 검색에서도 후보 ID로 반환되지 않고 unmappedCandidates 개수로만 집계됩니다. 기존 반려동물에 매핑을 추가하는 기능은 제공되지 않습니다.

사전 준비

  1. Petify Console에서 API Key를 발급합니다. (콘솔 Integration → API & SDK 탭에서도 관리할 수 있습니다.)
  2. 콘솔의 Integration → Links → 허용 Redirect URL에서 사용할 Redirect URL을 미리 등록합니다.

Server Integration은 허용 목록에 사전 등록된 Redirect URL만 사용할 수 있습니다(정확 일치). 미등록 URL로 세션을 발급하면 400 에러(PETNOWB2B21003)가 반환됩니다. 등록 규칙은 Redirect URL과 결과 전달을 참고하세요.

세션 발급

엔드포인트: POST /v2/hosted-sessions

요청

curl -X POST "https://api.petify.petnow.io/v2/hosted-sessions" \
  -H "x-petnow-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "REGISTER",
    "species": "DOG",
    "redirectUrl": "https://your-service.example.com/petify/return",
    "externalPetId": "YOUR-PET-ID-123",
    "expiresInSeconds": 1800,
    "locale": "ko"
  }'
import requests

response = requests.post(
    "https://api.petify.petnow.io/v2/hosted-sessions",
    headers={
        "x-petnow-api-key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "action": "REGISTER",
        "species": "DOG",
        "redirectUrl": "https://your-service.example.com/petify/return",
        "externalPetId": "YOUR-PET-ID-123",
        "expiresInSeconds": 1800,
        "locale": "ko",
    },
)
session = response.json()["data"]
# session["id"]  → 폴링 핸들 (반드시 저장)
# session["url"] → 사용자에게 전달할 1회용 촬영 URL
const response = await fetch("https://api.petify.petnow.io/v2/hosted-sessions", {
  method: "POST",
  headers: {
    "x-petnow-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    action: "REGISTER",
    species: "DOG",
    redirectUrl: "https://your-service.example.com/petify/return",
    externalPetId: "YOUR-PET-ID-123",
    expiresInSeconds: 1800,
    locale: "ko"
  })
});

const { data: session } = await response.json();
// session.id  → 폴링 핸들 (반드시 저장)
// session.url → 사용자에게 전달할 1회용 촬영 URL

요청 파라미터

필드타입필수설명
actionstringREGISTER(등록) / VERIFY(인증) / IDENTIFY(검색)
speciesstringDOG 또는 CAT. VERIFY는 대상 반려동물의 종과 일치해야 하고, IDENTIFY는 이 종으로 등록된 반려동물 중에서만 후보를 찾습니다
redirectUrlstring완료 후 복귀할 URL. 허용 목록과 정확히 일치해야 합니다
externalPetIdstring조건부고객사 시스템의 반려동물 ID (최대 255자). REGISTER/VERIFY는 필수, IDENTIFY는 보내면 안 됩니다
petMetadatastringREGISTER 전용. 등록되는 반려동물의 metadata 필드에 저장할 자유 형식 문자열
expiresInSecondsinteger세션 만료 시간 (300–86400초, 기본 1800)
localestring촬영 페이지 언어 힌트 (ko 기본, en 지원)

응답 (201)

{
  "success": true,
  "data": {
    "id": "8f6a1f9e-4c2d-4b7a-9e1a-2f3b4c5d6e7f",
    "url": "https://capture.petify.petnow.io/s/hs_QzR4c2VjcmV0dG9rZW4",
    "action": "REGISTER",
    "species": "DOG",
    "integrationMethod": "SERVER_INTEGRATION",
    "bindingSource": "SERVER_ISSUED",
    "externalPetId": "YOUR-PET-ID-123",
    "expiresAt": "2026-08-26T05:30:00Z",
    "createdAt": "2026-08-26T05:00:00Z"
  }
}
  • id반드시 저장하세요 — 발급 시점에 자체 사용자·주문 정보와 함께 저장해 두면 결과의 sessionId·externalPetId를 그 값과 대응시켜 어떤 사용자의 건인지 확정할 수 있고, 리다이렉트가 유실됐을 때 결과를 조회할 유일한 수단이기도 합니다.
  • url발급 응답에서 한 번만 제공되며 다시 조회할 수 없습니다. 1회용으로 취급하세요 — 같은 URL을 여러 채널로 재전송하는 설계는 피해야 합니다.

결과 수신

촬영이 끝나면 사용자 브라우저가 redirectUrl로 이동하며 petify_status(및 조건부 petify_code)가 쿼리 파라미터로 추가됩니다. 파라미터 규칙 전체는 Redirect URL과 결과 전달을 참고하세요.

결과 코드 소비

petify_status=completed로 받은 1회용 결과 코드를 서버에서 교환합니다.

엔드포인트: GET /v2/hosted-results/{code}

curl -X GET "https://api.petify.petnow.io/v2/hosted-results/hrc_1a2b3c4d5e6f" \
  -H "x-petnow-api-key: YOUR_API_KEY"
import requests

response = requests.get(
    "https://api.petify.petnow.io/v2/hosted-results/hrc_1a2b3c4d5e6f",
    headers={"x-petnow-api-key": "YOUR_API_KEY"},
)
result = response.json()["data"]
const response = await fetch(
  "https://api.petify.petnow.io/v2/hosted-results/hrc_1a2b3c4d5e6f",
  { headers: { "x-petnow-api-key": "YOUR_API_KEY" } }
);
const { data: result } = await response.json();

응답 예시 (인증):

{
  "success": true,
  "data": {
    "sessionId": "8f6a1f9e-4c2d-4b7a-9e1a-2f3b4c5d6e7f",
    "action": "VERIFY",
    "externalPetId": "YOUR-PET-ID-123",
    "bindingTrusted": true,
    "outcome": "MATCH",
    "completedAt": "2026-08-26T05:10:00Z",
    "alreadyConsumed": false,
    "result": {
      "isVerified": true,
      "score": 93
    }
  }
}

기능별 result 스키마:

기능필드설명
REGISTERregistered (boolean)등록 성공 여부
VERIFYisVerified (boolean), score (0–100)일치 여부와 점수
IDENTIFYcandidates (배열), unmappedCandidates (integer)후보 목록 [{ externalPetId, score }] (점수 내림차순). externalPetId 매핑이 없는 후보는 목록에서 제외되고 unmappedCandidates에 개수로 집계됩니다

소비 규칙:

  • 결과 코드 조회는 1회 소비(one-shot consume) 입니다. 코드는 세션 완료 후 약 10분간 유효합니다.
  • 소비에 성공했지만 응답을 유실한 경우(타임아웃, 장애), 같은 API Key로 5분(기본) 안에 다시 조회하면 alreadyConsumed: true와 함께 동일한 결과가 반환됩니다. 그 이후에는 410이 반환됩니다.
  • 코드 자체를 유실했다면 아래의 세션 폴링으로 대체하세요.

세션 상태 폴링

리다이렉트·코드 유실 시의 복구 경로이자, 세션 진행 상태를 확인하는 수단입니다. 소비형이 아니므로 여러 번 호출해도 됩니다.

엔드포인트: GET /v2/hosted-sessions/{id}

{
  "success": true,
  "data": {
    "id": "8f6a1f9e-4c2d-4b7a-9e1a-2f3b4c5d6e7f",
    "linkId": null,
    "action": "VERIFY",
    "species": "DOG",
    "integrationMethod": "SERVER_INTEGRATION",
    "bindingSource": "SERVER_ISSUED",
    "externalPetId": "YOUR-PET-ID-123",
    "bindingTrusted": true,
    "status": "COMPLETED",
    "outcome": "MATCH",
    "errorCode": null,
    "result": { "isVerified": true, "score": 93 },
    "expiresAt": "2026-08-26T05:30:00Z",
    "completedAt": "2026-08-26T05:10:00Z",
    "createdAt": "2026-08-26T05:00:00Z"
  }
}

세션 상태 (status):

상태설명
CREATED세션이 발급되었고 아직 열리지 않음
OPENED사용자가 촬영 페이지를 열었음
PROCESSING제출 후 처리 중
COMPLETED정상 완료 — outcomeresult가 채워짐
FAILED실패 — errorCode에 사유가 담김 (예: CAPTURE_FAILED)
EXPIRED완료되지 않고 만료됨
CANCELLED사용자가 촬영을 취소함
  • outcomeCOMPLETED일 때만 채워집니다: 등록 SUCCEEDED, 인증·검색 MATCH / NO_MATCH.
  • 이 엔드포인트는 Link Integration 링크로 생성된 세션도 조회할 수 있습니다 (링크 세션은 linkId가 채워지고, 사용자 전달 externalPetIdbindingTrusted: false).

에러 코드

에러 응답은 { "errors": [{ "code": "..." }] } 형식입니다. PETNOWB2B10001(요청 검증 실패)은 details 필드로 필드별 오류를 함께 반환합니다.

코드HTTP의미
PETNOWB2B10000401API Key 누락 또는 잘못됨
PETNOWB2B10001400요청 검증 실패 (details에 필드별 사유)
PETNOWB2B10005402등록 가능한 반려동물 수 한도 도달
PETNOWB2B10010403결제 문제로 계정이 정지됨
PETNOWB2B10011402현재 플랜에 검색(IDENTIFY) 기능이 포함되지 않음
PETNOWB2B10012402플랜 선택이 필요함 (콘솔에서 플랜 선택 후 사용 가능)
PETNOWB2B20015400VERIFY 대상 반려동물과 species 불일치
PETNOWB2B21001409REGISTER: 이미 등록에 사용된 externalPetId
PETNOWB2B21002404VERIFY: 등록 이력이 없는 externalPetId
PETNOWB2B21003400redirectUrl이 허용 목록에 없음
PETNOWB2B21004404세션을 찾을 수 없음
PETNOWB2B21005404결과 코드가 없거나 만료됨 (Link Integration 표시용 코드도 이 경로에서는 404)
PETNOWB2B21006410이미 소비된 결과 코드 (유예 시간 경과)
PETNOWB2B21007503세션 발급이 일시적으로 중단됨 — 짧은 간격의 재시도 없이 백오프 후 재시도하세요

다음 단계

On this page