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

요청 파라미터

필드타입필수설명
actionstring✅REGISTER(등록) / VERIFY(인증) / IDENTIFY(검색)
speciesstring✅DOG 또는 CAT. VERIFY는 대상 반려동물의 종과 일치해야 하고, IDENTIFY는 이 종으로 등록된 반려동물 중에서만 후보를 찾습니다
redirectUrlstring✅완료 후 복귀할 URL. 허용 목록과 정확히 일치해야 합니다
externalPetIdstring조건부고객사 시스템의 반려동물 ID (최대 255자). REGISTER/VERIFY는 필수, IDENTIFY는 보내면 안 됩니다
petMetadatastring❌REGISTER 전용. 등록되는 반려동물의 metadata 필드에 저장할 자유 형식 문자열
expiresInSecondsinteger❌세션 만료 시간 (300–86400초, 기본 1800)
localestring❌촬영 페이지 언어 힌트 (ko 기본, en 지원)
mockboolean❌체험(mock) 세션을 발급합니다 (기본 false). 사용자가 카메라 대신 내장된 샘플 반려동물을 선택하며, 그 이후는 실제 파이프라인입니다. 응답에 그대로 echo됩니다 — 체험(mock) 세션 참고

응답 (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",
    "mock": false
  }
}
  • id는 반드시 저장하세요 — 발급 시점에 자체 사용자·주문 정보와 함께 저장해 두면 결과의 sessionId·externalPetId를 그 값과 대응시켜 어떤 사용자의 건인지 확정할 수 있고, 리다이렉트가 유실됐을 때 결과를 조회할 유일한 수단이기도 합니다.
  • url은 발급 응답에서 한 번만 제공되며 다시 조회할 수 없습니다. 각 url은 한 명의 사용자에게 한 번만 전달하세요 — 같은 URL을 여러 채널로 재전송하는 설계는 피해야 합니다. 전달에 실패하면 재전송 대신 새 세션을 발급하세요.

체험(mock) 세션

"mock": true로 발급하면 실제 동물 없이 연동을 검증·평가할 수 있는 체험 세션이 됩니다. url을 연 사용자는 카메라 대신 내장된 샘플 반려동물을 선택하며, 그 이후 — 품질 검사, 등록·매칭, 소비·폴링하는 결과, 리다이렉트, 과금, 상한 — 는 모두 실제 파이프라인입니다.

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": "TEST-PET-001",
    "mock": true
  }'
  • 등록된 반려동물과 결과에는 mock 표시가 어디에도 남지 않습니다 — 이 요청/응답 쌍에만 있습니다. mock 등록도 실제 과금 대상 등록이므로, 테스트 후 콘솔에서 테스트 반려동물을 삭제하세요 (Petify Console 참고).
  • externalPetId 규칙은 동일합니다: ID는 고객사 서버가 직접 발급합니다.
  • mock url을 실제 사용자에게 전달하지 마세요.

결과 수신

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

호스팅 세션이 만든 작업은 현재 Webhook 이벤트를 보내지 않으므로, 아래의 결과 코드와 세션 폴링을 사용하세요.

결과 코드 소비

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정상 완료 — outcome과 result가 채워짐
FAILED실패 — errorCode에 사유가 담김 (아래 참고)
EXPIRED완료되지 않고 만료됨
CANCELLED사용자가 촬영을 취소함
  • outcome은 COMPLETED일 때만 채워집니다: 등록 SUCCEEDED, 인증·검색 MATCH / NO_MATCH.

  • FAILED의 errorCode는 닫힌 enum이 아닌 일반 문자열이므로 목록이 늘어날 수 있다고 가정하세요. 현재 값:

    errorCode의미
    CAPTURE_FAILED촬영에서 결과를 만들지 못함 (예: 품질 부족)
    DUPLICATE_EXTERNAL_PET_ID완료 시점에 open 모드의 중복 externalPetId가 감지됨
    IDENTIFICATION_NOT_IN_PLAN / IDENTIFICATION_PLAN_SELECTION_REQUIREDIDENTIFY 세션이 발급과 최종 제출 사이에 검색 권한을 잃음 (예: 사용자가 촬영하는 동안 플랜 변경). 제출이 거부되고 결과는 생성되지 않으며 과금되지 않습니다. 브라우저 리다이렉트에는 petify_code=FEATURE_UNAVAILABLE이 붙습니다. 이 코드들은 향후 릴리즈부터 반환될 수 있습니다. 지금 미리 처리해 두면 전환 시 영향이 없습니다
  • 이 엔드포인트는 Link Integration 링크로 생성된 세션도 조회할 수 있습니다 (링크 세션은 linkId가 채워지고, 사용자 전달 externalPetId는 bindingTrusted: 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이미 소비된 결과 코드 (유예 시간 경과)

다음 단계

On this page