DEVELOPER RESOURCES

고객사 연동규약

MP3 회화 분석부터 앱 재녹음과 결과 조회까지.
고객사 서비스에 배로를 연결하기 위한 API 규격입니다.

v2.1 · 2026-10-02
API 목차1. 공통 규칙2. 연동 순서와 API3. MP3 제출4. 회화 분석 상태·결과 조회5. 연습 후보 조회6. 앱 연결 링크 발급7. 재녹음 결과 조회8. 결과 화면 연결9. 오류·재시도부록. 학습자 화자 지정 — 선택

1. 공통 규칙

항목 규약
Base URL https://baerro-api.learnex.kr
인증 모든 고객사 API에 X-API-KEY: <고객사 키> 헤더 사용
호출 위치 고객사 백엔드. 브라우저·앱에 고객사 키를 전달하지 않음
학생 식별 consumer_id 또는 student_email
응답 형식 JSON. 추가 필드는 무시하고 null·빈 배열을 처리
접근 제어 고객사 서버에서 로그인 사용자가 해당 학생·통화의 소유자인지 확인

연동 전에 고객사 키, 학생 식별 방식, 연습 후보 기능 사용 여부를 배로 담당자와 확인합니다. 배로 학생 페이지를 사용할 경우 고객사 slug, 학생 로그인 정보와 지원 앱 설치 안내도 필요합니다. 파일 크기·녹음 길이·동시 처리량·보관 기간은 제공 조건을 확인합니다.

2. 연동 순서와 API

순서 메서드 경로 용도
1 POST /api/conversation/analyze MP3 제출
2 GET /api/conversation/{analysis_id} 회화 분석 상태·결과 조회
3 GET /api/conversation/{analysis_id}/pronunciation-review 연습 후보 조회
4 POST /api/conversation/{analysis_id}/pronunciation-review/practice-link 선택한 문장의 앱 연결 링크 발급
5 GET /api/conversation/{analysis_id}/pronunciation-review/practice-results 재녹음 결과 조회

회화 분석은 1~2단계로 연동합니다. 앱 재녹음 기능을 제공할 경우 3~5단계를 추가합니다. 회화 분석 완료, 연습 후보 준비, 재녹음 채점 완료는 별도 상태입니다. 회화 분석이 완료되어도 후보가 없을 수 있습니다.

3. MP3 제출

POST /api/conversation/analyze · multipart/form-data

필드 형식 필수 조건
consumer_id 문자열 student_email과 최소 하나. 둘 다 있으면 이 값 우선
student_email 문자열 해당 고객사 내 학생 조회·생성에 사용
file 파일 mp3_url과 정확히 하나
mp3_url 문자열 file과 정확히 하나. 배로 서버가 별도 인증 헤더 없이 다운로드 가능한 URL
curl --fail-with-body \
  -H "X-API-KEY: $BAERRO_API_KEY" \
  --form-string "student_email=student@example.com" \
  -F "file=@lesson.mp3;type=audio/mpeg" \
  https://baerro-api.learnex.kr/api/conversation/analyze

URL 입력 시 file 필드 대신 --form-string "mp3_url=https://storage.example.com/lesson.mp3"를 사용합니다. URL은 분석과 후속 처리 기간 동안 접근 가능해야 합니다. curl 예시는 Bash 기준입니다.

응답: HTTP 200

{
  "analysis_id": "11111111-1111-4111-8111-111111111111",
  "status": "processing",
  "report_url": "https://baerro-api.learnex.kr/reports/your-company/conversation/11111111-1111-4111-8111-111111111111"
}
  • analysis_id를 고객사 수업·녹음 ID와 함께 저장합니다.
  • HTTP 200은 접수 성공입니다. 분석 완료로 표시하지 않습니다.
  • report_url은 공개 회화 리포트 주소입니다. 고객사 slug가 없으면 null입니다. 화면 연동은 8절을 따릅니다.
  • 중복 요청 방지 키와 완료 웹훅은 지원하지 않습니다. 고객사 녹음 ID로 중복 제출을 방지하고 응답 유실 시 접수 여부를 확인한 뒤 재요청합니다.

4. 회화 분석 상태·결과 조회

GET /api/conversation/{analysis_id}

curl --fail-with-body \
  -H "X-API-KEY: $BAERRO_API_KEY" \
  "https://baerro-api.learnex.kr/api/conversation/$ANALYSIS_ID"
status 처리
processing 3~5초 간격으로 조회. 장시간 대기 시 간격을 늘리고 최대 대기시간 설정
completed 폴링 종료. 대화록·교정·코칭 표시
failed 폴링 종료. error_message 확인

완료 응답의 주요 필드

{
  "id": "11111111-1111-4111-8111-111111111111",
  "status": "completed",
  "transcript": {
    "turns": [
      {"speaker": "student", "start": "00:10", "end": "00:14", "text": "I would like to book a table."}
    ]
  },
  "analysis": {
    "corrections": [{"original": "원래 표현", "suggestion": "제안 표현"}],
    "summary": "회화 코칭 내용"
  },
  "error_message": null
}

speaker는 student 또는 teacher, 시간은 녹음 시작 기준 MM:SS입니다. 문장·화자·시간은 자동 추정값입니다. transcript, analysis의 null과 빈 교정 목록을 처리하고, 응답 문자열은 텍스트로 렌더링합니다.

5. 연습 후보 조회

GET /api/conversation/{analysis_id}/pronunciation-review

후보 기능은 고객사별 제공 설정이 필요합니다. 결과 미생성 시 HTTP 200과 아래 응답을 반환합니다.

{
  "analysis_id": "11111111-1111-4111-8111-111111111111",
  "status": "not_available",
  "analysis_status": "completed",
  "publishable": false,
  "reason": "review_artifact_not_created",
  "job": null
}

job이 존재하면 state, attempts, error_code를 포함할 수 있습니다. state가 queued, processing, retry_wait이면 제한적으로 재조회합니다. job: null 또는 failed이면 폴링을 멈추고 ‘연습 문장 준비 안 됨’을 표시합니다.

준비된 응답은 status: "review_ready"이며 다음 필드를 사용합니다.

필드 사용 방법
revision 64자리 해시. 링크 발급 시 그대로 전달
role_version 정수. 링크 발급 시 그대로 전달
practice_candidates 고객사 확인 역할에 따른 후보 배열
automatic_practice_candidates 자동 역할 추정에 따른 후보 배열
publishable / pronunciation_score 현재 false / null. 통화 발음 성적으로 표시하지 않음

두 후보 배열을 합쳐 표시하고 segment_id로 식별합니다. 후보 객체의 주요 필드 예시:

{
  "segment_id": "segment-example",
  "start_seconds": 97.0,
  "end_seconds": 104.0,
  "text": "I would like to book a table.",
  "speaker_id": "SPEAKER_01",
  "suggested_words": [{"word_index": 4, "word": "book"}],
  "is_confirmed_error": false
}

start_seconds, end_seconds는 녹음 시작 기준 초입니다. word_index는 구간 단어 배열의 0 기반 위치입니다. 후보는 확정된 오발음이 아니므로 ‘다시 읽기’로 안내하고 문장이 실제 발화와 다르면 건너뛸 수 있게 합니다. 빈 후보를 점수 0이나 우수 판정으로 처리하지 않습니다.

6. 앱 연결 링크 발급

학습자가 후보를 선택했을 때 호출합니다.

POST /api/conversation/{analysis_id}/pronunciation-review/practice-link · application/json

{
  "revision": "<후보 조회에서 받은 64자리 해시>",
  "role_version": 0,
  "segment_id": "<선택한 후보의 segment_id>"
}

응답: HTTP 200

{
  "deep_link": "baerro://pronunciation?mode=reread&ticket=<서명된_토큰>",
  "expires_in_seconds": 900,
  "purpose": "optional_rereading",
  "assessment_created": false
}
  • 응답의 deep_link 전체를 변경 없이 해당 학습자에게 전달합니다. 유효기간은 15분입니다.
  • 링크는 학생 연결 권한을 포함하므로 로그·공유 게시판·영구 저장소에 남기지 않습니다.
  • 지원 버전 배로 앱이 필요합니다. 휴대폰에서 링크를 열고, PC에서는 본인 휴대폰으로 열도록 안내합니다.
  • 링크 발급만으로 채점되지 않습니다. 학습자가 앱에서 문장을 확인하고 새로 녹음·제출해야 합니다. 앱 인증과 녹음 업로드는 배로 앱이 처리합니다.
  • 409이면 후보를 다시 조회하고 재선택하도록 합니다. 앱에서 만료 안내가 나오면 새 링크를 발급합니다.

7. 재녹음 결과 조회

GET /api/conversation/{analysis_id}/pronunciation-review/practice-results

해당 통화에 연결된 재녹음 기록을 최신순 최대 200건 반환합니다. 페이지네이션은 제공하지 않습니다.

{
  "analysis_id": "11111111-1111-4111-8111-111111111111",
  "limit": 200,
  "sessions": [
    {
      "id": "22222222-2222-4222-8222-222222222222",
      "status": "scored",
      "reference_text": "I would like to book a table.",
      "created_at": "2026-10-02T04:00:00+00:00",
      "source_segment_id": "segment-example",
      "source_revision": "<연습 시작 시점의 revision>",
      "source_role_version": 0,
      "presentation": {
        "version": "recognition-reference-v1",
        "label": "인식 참고값",
        "value": 80.0,
        "word_values": [80.0, 80.0, 80.0, 80.0, 80.0, 80.0, 80.0],
        "validated": false,
        "notice": "음성 인식 모델의 참고값이며 발음 성적이 아니에요. 녹음 환경에 따라 달라질 수 있어요."
      }
    }
  ]
}
상태·필드 화면 처리
sessions: [] 연결된 연습 기록 없음
status: scored 채점 완료. presentation 표시
status: failed 채점 실패. 재녹음 안내
status: expired 녹음 보관 기간 만료
그 외 진행 상태 처리 중 표시. 제한적으로 재조회
presentation 또는 value가 null ‘값 없음’ 표시. 0으로 대체하지 않음

created_at은 시간대가 포함된 날짜 문자열입니다. 사용자 시간대로 표시합니다. 현재 후보와 source_revision 또는 source_role_version이 다르면 ‘이전 후보의 연습 기록’으로 구분합니다. 긴 후보 문장은 앱에서 나뉘므로 reference_text가 원본의 일부일 수 있습니다.

결과는 새 녹음의 결과이며 원본 통화 점수가 아닙니다. label, notice와 함께 표시하고, value를 정답률·발음 등급으로 바꾸지 않습니다. 서로 다른 presentation.version의 값을 같은 척도로 비교하지 않습니다. 이 API에는 음성 다운로드 URL이 포함되지 않습니다.

8. 결과 화면 연결

방식 주소·처리
고객사 자체 화면 고객사 서버에서 API 조회 후 본인 결과만 화면에 전달
배로 학생 페이지 https://baerro.app/s/{slug}/conversation/{analysis_id} — 학생 로그인 필요. 후보·앱 연결·재녹음 이력 제공
공개 회화 리포트 접수 응답의 report_url — 로그인 없이 링크 소지자가 조회 가능. 재녹음 이력 화면과 별개

앱 연습과 이력을 함께 제공하려면 고객사 자체 화면 또는 배로 학생 페이지를 사용합니다. 학생 이메일로 분석을 제출하는 것만으로 포털 로그인 계정이 설정되지는 않습니다. 고객사 로그인과 배로 학생 로그인은 별개입니다.

공개 report_url은 해당 학생에게만 전달합니다. 링크 자체의 만료 기능은 없습니다. report_url: null이면 링크 버튼을 숨깁니다. 배로 학생 페이지에서는 ‘후보 새로 확인’으로 재녹음 이력을 갱신합니다.

공개 결과 링크 통일 (2026-10-14)

결과 버튼·문자·메일에 넣을 주소는 API 응답의 report_url을 그대로 저장하고 사용합니다. 발급 주소는 https://baerro-api.learnex.kr/reports/{slug}/conversation/{analysis_id}로 통일됩니다. baerro-1.learnex.kr/result.html이나 API 도메인의 /result.html로 주소를 조합하지 않습니다. PUBLIC_BASE_URL의 레거시 설정은 공개 회화 리포트 발급 주소에 영향을 주지 않습니다.

  • 새 분석: POST /api/conversation/analyze의 analysis_id, report_url을 고객사 서버에 함께 저장합니다.
  • 기존 분석: 고객사 서버에서 인증된 GET /api/conversation/{analysis_id}를 호출하고 반환된 report_url로 저장 링크를 갱신합니다. 재분석은 필요하지 않습니다.
  • 다른 고객사 분석은 404이며, slug가 없는 고객사는 report_url: null입니다. 임의의 대체 주소를 만들지 않습니다.
  • 고객사 서버는 로그인한 학생의 소유권을 확인한 뒤 해당 학생의 report_url만 화면에 전달합니다. 고객사 API 키를 브라우저·URL·메일에 넣지 않습니다.
  • 이미 발송된 기존 링크는 자동으로 바뀌지 않습니다. 기존 HTTPS 도메인의 인증서 오류는 리다이렉트만으로 해결되지 않습니다.

화면 연동은 report-link.mjs를 프로젝트에 복사해 사용합니다. 공식 HTTPS 리포트 URL만 허용하고 null·잘못된 URL은 기존 링크를 제거한 뒤 버튼을 숨깁니다.

<a id="conversation-report" hidden>분석 리포트 보기</a>
import { bindReportLink } from './report-link.mjs';

// 고객사 서버가 학생 본인 확인 후 전달한 API 응답 데이터입니다.
// 접수 및 상태 조회 응답 모두 같은 함수를 사용합니다.
export function updateReportButton(analysisResponse) {
  bindReportLink(document.querySelector('#conversation-report'), analysisResponse);
}

문자·메일도 같은 report_url을 사용하고, HTML 메일에서는 템플릿의 속성 이스케이프를 적용합니다. 이 규약을 적용하려면 API 변경과 고객사 화면/알림 코드 변경을 각각 배포해야 합니다.

9. 오류·재시도

오류 본문은 보통 {"detail":"설명"}이며, 422의 detail은 필드별 검증 오류 배열일 수 있습니다.

HTTP / 상태 처리
400 필수값·학생 식별자·음성 입력 중복/누락 확인
401 / 403 인증·고객사 활성 상태·학생 접근 권한 확인
404 분석 ID와 고객사·학생 매핑 확인
409 결과 미준비 또는 버전 변경. 최신 후보 조회 후 재선택
422 요청 필드·자료형·허용 값 수정
500 / 503 / 타임아웃 GET은 간격을 늘려 제한적으로 재시도. POST는 접수 여부 확인 후 재시도
HTTP 200 + failed 비동기 처리 실패. 폴링 종료
HTTP 200 + not_available 후보 미생성. 5절의 job 상태에 따라 처리

자동 재시도에는 최대 횟수·대기시간을 둡니다. 문의 시 analysis_id, 요청 시각, API 경로와 HTTP 상태를 전달하고 비밀키·앱 연결 토큰은 제외합니다.

부록. 학습자 화자 지정 — 선택

혼합 녹음에서 특정 화자가 학습자임을 확인할 수 있는 고객사만 사용합니다. 자동 후보를 이용하는 기본 연동에는 필요하지 않습니다.

PUT /api/conversation/{analysis_id}/pronunciation-review/learner-role · application/json

{
  "revision": "<현재 revision>",
  "role_version": 0,
  "learner_speaker": "<현재 조회 응답의 speaker_id>",
  "evidence_type": "recording_anchor",
  "evidence_ref": "customer-internal-reference"
}

evidence_type은 customer_metadata 또는 recording_anchor, evidence_ref는 1~240자의 내부 근거 참조값입니다. 자동 추정만으로 확인 처리하지 않으며 비밀키·연락처·녹음 URL을 근거 필드에 넣지 않습니다.

취소는 learner_speaker: null과 현재 revision, role_version을 보내고 근거 필드를 생략합니다. 성공 응답은 최신 후보 조회와 같은 구조입니다. 변경·취소 후 버전이 증가하므로 후보를 갱신해야 합니다. 취소 시 자동 후보가 다시 활성화된다고 가정하지 않습니다. 409이면 최신 상태를 확인하고 다시 판단합니다.

운영 OpenAPI 스키마