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이면 최신 상태를 확인하고 다시 판단합니다.