Интеграция и API19 мин

HTTP кодове в бизнес API: какво да върнете, когато търсят контакт, и колко струва грешката

От · основател и главен изпълнителен директор на Hábil ·

Кога да върнете 200, 404, 400, 422, 409, 429 или 503 при търсене на контакт и колко струва грешният избор: повторни опити, подвеждащи табла и агенти.

Интегратор на Вашата компания извиква API, за да потърси контакт по имейл адрес. Отговорът идва с код 200 и тяло, което гласи {"error": "контактът не е намерен"}. Програмата на интегратора вижда 200, приема операцията за успешна и продължава с контакт, който не съществува. Никой не разбира нищо, докато седмици по-късно някой не попита защо липсват поръчки.

Нищо не се е счупило в мрежата, нито в сървъра, нито в базата данни. Грешката е в едно решение, което изглежда дребно: кой номер да стои на първия ред на отговора. Кодът за състояние е единственото, което, без да го тълкуват, четат балансьорите на натоварването, таблата за мониторинг, библиотеките за повторни опити, шлюзовете и програмите на Вашите партньори. Изречението в тялото го разбира човек; номера го разбира всичко останало.

Статията е за технически директори и ръководители на технологични екипи и е написана така, че директор, който я прегледа бегло, да вземе същественото от таблиците и от заключенията на всеки раздел. С един-единствен пример, търсенето на контакт, тя обхваща кодовете, които наистина се използват в бизнес API, и обяснява кога кой е уместен и колко струва грешката. Техническите твърдения се опират на стандартите на HTTP и са цитирани в края; проектантските критерии, които са наши, са отбелязани като такива.

Един HTTP отговор носи код, заглавия и тяло. Кодът и заглавията са стандартизирани сигнали, по които много инструменти вземат решения; тялото го чете човек или, ако договорът го определя, програма.

HTTP кодове в бизнес API: какво да върнете, когато търсят контакт, и колко струва грешката
Ако отговорът е…Семейството е…Общо правило
Истински успех2xxПродължавате напред
Заявка, която трябва да се поправи4xxПоправяте, идентифицирате се или изчаквате, според кода и договора
Грешка на сървъра или на зависима система5xxПовтаряте само ако операцията може да се повтаря или договорът предлага проверима идемпотентност

Трите части на един отговор и защо номерът е водещ

В HTTP/1.1 отговорът започва с ред за състоянието: кода и, по желание, фраза; в HTTP/2 и по-късните версии кодът пътува като метаданни на отговора, със същото значение. Стандартът уточнява, че фразите са само препоръки: могат да се заменят с местни еквиваленти или да се пропуснат, без да се засяга протоколът [1, §15.1]. Значение има номерът.

Първата цифра определя семейството и стандартът изисква от всеки клиент да го разбира, дори да не познава точния код [1, §15]:

Трите части на един отговор и защо номерът е водещ
СемействоЗначение в стандартаКакво казва на този, който извиква
2xxЗаявката е получена, разбрана и приетаПродължете напред
3xxНужно е още едно действие, за да се завършиИдете другаде или използвайте онова, което вече имате
4xxЗаявката съдържа грешка или не може да бъде изпълненаВижте конкретния код и договора; повторението на същата заявка почти никога не помага
5xxСървърът не е успял да обслужи привидно валидна заявкаВижте конкретния код и договора; не повтаряйте на сляпо запис, който не е идемпотентен

Има правило, което обяснява защо верният избор е по-важен от подробния избор: клиент, който получи непознат за него код, трябва да го третира като x00 на неговото семейство. Самият стандарт го онагледява: хипотетичният 471 се третира като 400 [1, §15]. С други думи, семейството е най-малкото, което всяка машина ще разбере. Ако поставите 200 на една грешка, семейството, което машините четат, е това на успеха.

Заглавията допълват номера. Някои са задължителни при определени кодове: 401 трябва да носи WWW-Authenticate, а 405 трябва да носи Allow с методите, които ресурсът приема [1, §15.5.2 и §15.5.6]. А тялото, както ще се види по-нататък, е добре да има стандартен формат, за да не се налага на програмата да гадае.

Колко от отговорите с грешка на сегашните Ви API се различават от успеха само по първия ред?

Случаят: търсене на контакт

Представете си API за контакти на една компания, за нуждите на нейните канали и партньори. То предлага два начина да се намери човек:

GET /contactos/{id}                 -> obtener un contacto por su identificador
GET /contactos?correo=ana@ejemplo.com -> buscar contactos por un criterio

Изглеждат като едно и също. Не са, а разликата вече поражда първото решение. С идентификатора този, който извиква, твърди: «този контакт съществува; дайте ми го». С търсенето той пита: «има ли такъв, който отговаря на това?». А правилният отговор на въпрос, който допуска «никой», не е грешка.

Договор в стил OpenAPI, опростен, за да имате цялата карта, преди да я обходим:

paths:
  /contactos/{id}:
    get:
      summary: Obtener un contacto por identificador
      responses:
        "200": { description: El contacto }
        "400": { description: El identificador no tiene forma válida }
        "401": { description: Falta identificarse }
        "403": { description: Identificado, pero sin permiso }
        "404": { description: No existe, o no se revela que exista }
        "410": { description: Existió y se retiró de forma definitiva }
        "429": { description: Demasiadas peticiones; ver Retry-After }
        "503": { description: Servicio no disponible por ahora; ver Retry-After }
  /contactos:
    get:
      summary: Buscar contactos por criterio
      responses:
        "200": { description: Lista de coincidencias (puede estar vacía) }
        "400": { description: Criterio ilegible o parámetro desconocido }
        "422": { description: Criterio bien formado, pero que el negocio no admite }
    post:
      summary: Crear un contacto
      responses:
        "201": { description: Creado; la cabecera Location apunta a él }
        "409": { description: Ya existe uno que choca con este }
        "422": { description: Datos bien formados pero que incumplen una regla }

Ще ги обходим от по-малко към по-сериозно, като започнем с онова, което завършва добре.

Ако екипът Ви трябваше да начертае тази карта за API на клиентите или на полиците, щяха ли всички да са съгласни колко различни отговора трябва да може да дава?

Когато всичко е наред: 200 и изкушението да се използва за всичко

Търсенето без съвпадения заслужава внимание. Заявка като тази:

curl -i "https://api.ejemplo.com/contactos?correo=nadie@ejemplo.com"

трябва да получи отговор:

HTTP/1.1 200 OK
Content-Type: application/json

{ "resultados": [], "total": 0 }

200 е кодът за успеха на една справка, а стандартът определя съдържанието му за GET като представяне на целевия ресурс [1, §15.3.1]. Тук целевият ресурс е търсенето, което съществува и е изпълнено правилно; резултатът му е празен списък. Това е разумно четене на определението и е наш проектантски критерий: важното е да се реши, да се документира и да се прилага еднакво във всички маршрути. Ако един маршрут връща празен списък, а друг връща 404 за същия вид въпрос, клиентът ще трябва да пише специален случай за всеки маршрут.

Обратното, грешката, маскирана като успех, е източникът на най-скъпите за откриване неизправности:

HTTP/1.1 200 OK
Content-Type: application/json

{ "error": "contacto no encontrado" }

Монитор, който гледа само кода за състояние, ще отчете това като здравословно. Библиотека за повторни опити ще го приеме за разрешено. Партньор, който интегрира Вашето API, ще валидира тялото само ако някой му е казал да го прави. 200 с грешка вътре е повреда, влязла през входа на офиса със служебната карта на успеха.

Има три сродни на 200 кода, които си струва да се различават, когато случаят го изисква, и ще им отделим по един ред: 201, когато нещо е създадено (обикновено с адреса на новия ресурс в Location), 202, когато работата е приета, но още не е завършила (ако договорът публикува справка за състоянието, проверява се там), и 204, когато всичко е минало добре и няма какво да се върне, например при премахване на контакт [1, §15.3].

Знае ли организацията Ви какъв процент от онова, което таблата Ви наричат «успех», съдържа тела с грешки вътре?

Когато клиентът е сгрешил: семейството 4xx

Семейството 4xx казва «проблемът е в заявката». За да се избере в него, може да помогне политика за предимство, тоест ред на въпросите, който намалява споровете. Това е наш критерий, не изискване на стандарта, и един договор може да го промени (например автентикацията преди валидирането е законно решение за сигурност):

  1. Разбира ли се заявката? (400)
  2. Знаем ли кой е? (401)
  3. Има ли разрешение? (403)
  4. Съществува ли онова, което иска? (404, 410)
  5. Допуска ли се този метод върху този ресурс? (405)
  6. Идва ли във формат, който приемаме? (415)
  7. Спазва ли съдържанието, вече разбрано, бизнес правилата? (422)
  8. Влиза ли в конфликт с онова, което вече е записано? (409)

С тази политика, когато една заявка има два дефекта, отговаря първият от списъка, който не е изпълнен. Това прави отговорите предвидими, а предвидимото може да се тества и документира.

400: не се разбира

Стандартът описва 400 като отговор, когато сървърът не може или не иска да обработи заявката поради нещо, което възприема като грешка на клиента: например неправилен синтаксис [1, §15.5.1]. В случая с контакта:

curl -i "https://api.ejemplo.com/contactos/abc%20%20%20"      # identificador ilegible
curl -i "https://api.ejemplo.com/contactos?correo=&color=azul" # parámetro desconocido

Ако договорът отхвърля тези стойности, и двете могат да получат 400. Сигналът е, че заявката не може да се прочете като онова, за което се представя. Повторена в същия вид, тя би дала същия резултат и затова добре направеният клиент не я повтаря: поправя я или я показва на човек.

401 и 403: кой сте Вие и какво можете да правите

Това са два различни въпроса и често се бъркат. 401 казва, че заявката не съдържа валидни идентификационни данни, и стандартът задължава да се включи WWW-Authenticate [1, §15.5.2]. 403 казва, че сървърът е разбрал заявката, но отказва да я обслужи; ако вече са изпратени идентификационни данни, клиентът не бива автоматично да повтаря със същите [1, §15.5.4].

Разликата има последици за експлоатацията. При 401 клиентът може да поднови сесията си и да повтори. При 403 повторението със същите данни не променя резултата. Сървър, който смесва двата кода, вкарва клиентите в цикъл на подновяване на данни, който нищо не решава.

Има един нюанс за сигурността, който е важен в API за контакти: стандартът позволява на сървър, който иска да скрие, че забранен ресурс съществува, да отговори 404 вместо 403 [1, §15.5.4]. Ако контактът на един клиент се вижда само от собственика му, думата «забранено» към друг клиент потвърждава, че този идентификатор съществува. «Не е намерено» не потвърждава нищо. Това е решение на политиката, което е добре да се запише, а не да се импровизира по маршрути.

404 и 410: няма го

404 показва, че не е намерено текущо представяне на ресурса или че сървърът не иска да разкрие, че съществува; не казва дали отсъствието е временно, или постоянно [1, §15.5.5]. Ако сървърът знае, че е постоянно, стандартът предпочита 410 [1, §15.5.11].

При контакта тази разлика има бизнес стойност. «Този идентификатор никога не е съществувал» (404) и «съществуваше и беше слят с друг или премахнат» (410) водят до различни действия от страна на клиента: при първото той проверява дали не е допуснал грешка при въвеждане; при второто спира да го иска и търси контакта, който го е заместил. Ако операцията Ви не различава двата случая, ползвайте 404, който е подразбиращият се, точно както препоръчва самият стандарт, когато сървърът не знае дали състоянието е постоянно [1, §15.5.11].

Един практически факт, който изненадва: 404 е сред кодовете, които стандартът определя като евристично кешируеми [1, §15.1]. Отговор «не съществува» може да остане запазен в междинен кеш, така че току-що създаден контакт да не се появява известно време. Ако създаването и справката следват непосредствено една след друга, добре е да се проверят заглавията за кеширане на този отговор.

405 и 415: маршрутът съществува, формата не

405 казва, че методът е познат, но този ресурс не го допуска, и изисква заглавие Allow с онова, което допуска [1, §15.5.6]. 415 казва, че форматът на изпратеното съдържание не се приема. Това са грешки на интеграцията, не на бизнеса, и се появяват най-вече през първите дни на нова връзка.

Когато партньор получи грешка от Вашето API, може ли да разбере по първия ред дали му е нужно да поправи заявката, да поднови сесията си или да спре да иска този ресурс?

400 срещу 422: разликата, която най-много се обсъжда

Най-честото обсъждане в екипите е кой от двата кода да се използва, когато данните са неверни. Политиката, която предлагаме за този договор (не е универсално правило на HTTP), опряна на определенията на стандарта, е кратка:

400 срещу 422: разликата, която най-много се обсъжда
СитуацияКодЗащо
JSON е повреден, липсва затваряща скоба или параметърът не съществува400Заявката не може да се прочете [1, §15.5.1]
JSON е валиден и форматът се приема, но имейлът няма «маймунско ухо» или телефонът не отговаря на правилото на неговата държава422Сървърът разбира съдържанието, синтаксисът е правилен, но не може да обработи онова, което се иска [1, §15.5.21]
Съдържанието е валидно, но влиза в конфликт с онова, което вече е записано (друг контакт със същия имейл)409Конфликт с текущото състояние на ресурса [1, §15.5.10]

422 се определя като отговор, при който сървърът разбира типа на съдържанието на заявката (затова 415 не се прилага) и синтаксисът ѝ е правилен, но не е могъл да обработи съдържащите се инструкции [1, §15.5.21]. Много екипи използват 400 за всичко и това не е грешка на протокола: 400 е широк. Предимството на разделянето е експлоатационно: при 400 клиентът знае, че неговият код е произвел нещо нечетимо (дефект на интеграцията), а при 422 знае, че данните не са минали едно правило (случай, който се показва на потребител). Две различни причини, две различни поправки, два различни екипа, които обработват сигнала.

Пример за създаване с невалидни данни:

curl -i -X POST https://api.ejemplo.com/contactos \
  -H "Content-Type: application/json" \
  -d '{"nombre": "Ana Pérez", "correo": "ana-at-ejemplo"}'
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://api.ejemplo.com/problemas/correo-invalido",
  "title": "El correo no tiene un formato válido",
  "status": 422,
  "detail": "El campo 'correo' debe contener una dirección de correo.",
  "instance": "/contactos"
}

Форматът на тялото не е случаен; обяснява се в раздела за Problem Details.

Ако днес отговаряте с 400 на всичко, може ли екипът Ви за поддръжка да различи лошо направена интеграция от погрешно въведени данни, без да отваря журналите?

409: когато се сблъсква с онова, което вече съществува

409 се запазва за заявка, която не може да бъде изпълнена поради конфликт с текущото състояние на ресурса, в ситуации, в които извикващият би могъл да го разреши и да изпрати отново; стандартът изисква сървърът да включи достатъчно информация, за да се разпознае източникът на конфликта [1, §15.5.10].

При контактите той се проявява по два начина. Първият е очевидният: създаване на контакт, чийто имейл вече принадлежи на друг. Вторият е по-скъп и по-малко видим: два канала редактират един и същи контакт почти едновременно. Ако и двата изпратят PUT с пълната версия, която са прочели, вторият, който пристигне, заличава мълчаливо промяната на първия. Никой не е видял грешка; системата е записала онова, което са ѝ поискали.

Стандартът на HTTP има инструменти за това: условните заявки. Договорът може да връща етикет за версия (ETag) при всяко четене и да изисква клиентът да го върне в If-Match при промяна; ако версията вече е различна, отговорът е 412 Precondition Failed [1, §15.5.13], а ако клиентът изобщо не е изпратил условието и сървърът го изисква, 428 Precondition Required, предвиден именно за да се избегне «загубената актуализация» [3, §3]. Така загубената промяна се превръща във видима грешка вместо в мълчалива дупка. Подробностите за всеки от тези два кода остават извън тази статия; важният тук принцип е: конфликтът може да се открие, а да се открие навреме е по-евтино, отколкото да се разбере при одит.

Какво се случва във Вашите системи, когато два канала променят един и същи клиент едновременно: има ли видима грешка, или печели последният?

429, 500, 502, 503 и 504: когато проблемът е на сървъра или на неговия капацитет

Семейството 5xx е мястото, където грешният избор струва най-много пари, защото практическият му ефект е да каже на машините «може да е добра идея да опитате пак». Доброто повторение спасява операцията; лошото я удвоява или събаря сървъра.

429: твърде много заявки

Макар да не е в основния стандарт на HTTP, а в едно разширение, 429 е правилният инструмент за ограничаване на темпото. Показва, че извикващият е изпратил твърде много заявки за даден период, и може да включва Retry-After, за да каже колко да изчака; същата спецификация уточнява, че не определя как се идентифицира потребителят, нито как се броят заявките, и че тези отговори не бива да се съхраняват в кеш [3, §4].

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json

{ "type": "https://api.ejemplo.com/problemas/limite-excedido",
  "title": "Se superó el límite de peticiones",
  "status": 429 }

Обърнете внимание, че 429 принадлежи към семейството на грешките на клиента, макар да засяга капацитета; може да отразява обща квота или политика, а не само темпото на извикващия. И обърнете внимание на Retry-After, който е по желание: ако идва, уважавайте го; ако не, следвайте документираната политика, защото без никакъв сигнал клиентът гадае. При интеграция, която запитва контакти на партиди, разликата е като между клиент, който се нагажда, и клиент, който настоява всяка секунда и влошава натоварването, което се е искало да се избегне.

500: не очаквахме това

500 показва неочаквано състояние, което е попречило да се изпълни заявката [1, §15.6.1]. Това е кодът на «възникна неочаквано състояние от наша страна»; сървърът може да знае причината, но отговорът не бива да я разкрива. Две правила за здравословен 500. Първо: тялото не бива да показва стека за изпълнение, заявката, пътищата до файлове или ключове; стандартът за Problem Details изрично предупреждава срещу излагането на подробности от реализацията, като дъмп на стека, защото могат да разкрият информация, която може да се използва срещу системата [2, §5]. Второ: съобщението трябва да е общо и да носи идентификатор, който екипът Ви може да потърси в журналите си.

502 и 504: един посредник не е получил отговор

Когато Вашето API зависи от друга система (CRM, ERP, основната система) и действа като шлюз или прокси, два кода описват какво се случва между двете (при други топологии договорът може да избере друг отговор). 502 е отговорът, когато сървърът е получил невалиден отговор от системата зад него; 504, когато не е получил отговор навреме [1, §15.6.3 и §15.6.5].

502 или 504 при запис е особено деликатен, и същото важи за 500 или 503, които идват, след като операцията вече е приложена, защото не казва дали операцията е приложена, или не. Ако клиентът повтори на сляпо създаването на контакт, може да създаде два. Изходът е в раздела за повторните опити.

503: сега не мога, върнете се след малко

503 показва, че сървърът не може да обслужи заявката в този момент поради временно претоварване или планирана поддръжка, нещо, което вероятно ще отшуми с времето; може да е придружен от Retry-After [1, §15.6.4]. Същият стандарт уточнява, че съществуването му не задължава претоварен сървър да го използва: някои просто отказват връзката [1, §15.6.4].

Retry-After приема брой секунди или дата [1, §10.2.3]. При 503 показва колко се очаква услугата да не е достъпна за клиента [1, §10.2.3]. При планирана поддръжка тази информация позволява на интеграциите на Вашите партньори да се поставят на пауза подредено, вместо да се сриват на каскада.

Обобщение на семейството, с въпроса, който най-много боли във всеки случай:

503: сега не мога, върнете се след малко
КодКакво казваКакво трябва да направи извикващиятТипичната грешка
429Бързате прекаленоДа изчака колкото каже Retry-After и да се върнеДа не се изпрати Retry-After; клиентът настоява
500Нещо неочаквано се провалиДа повтори предпазливо, ако операцията може да се повтаря; да уведомиДа се покаже стек, заявка или пътища
502Системата зад него отговори нещо невалидноДа провери дали операцията е приложена, преди да повториДа се повтори на сляпо създаване
503В момента не мога да обслужаДа изчака и да повториДа се върне 200 с «в поддръжка» в тялото
504Системата зад него не отговори навремеДа провери състоянието, преди да повториДа се бърка с неуспех на клиента

Когато някоя от вътрешните Ви системи се забави, API-тата Ви казват ли на извикващия колко да чака, или го оставят да решава на късмет?

Problem Details (RFC 9457): тяло на грешката, което програма наистина може да прочете

След като кодовете са избрани, остава тялото. Без споразумение всяко API измисля свое: {"error": "..."}, {"mensaje": "..."}, {"codigo": 7}. Стандартът RFC 9457 (който заменя 7807) определя общ формат за това [2]. Идентифициран с медийния тип application/problem+json, той определя пет основни члена, които могат да се включат [2, §3.1]:

Problem Details (RFC 9457): тяло на грешката, което програма наистина може да прочете
ЧленЗа какво служи
typeИдентификатор на вида проблем, във вид на URI; потребителите трябва да го ползват като основен идентификатор на проблема [2, §3.1.1]
titleКратко резюме на вида проблем, четимо от хора [2, §3.1.3]
statusHTTP кодът; информативен е и създателят трябва да използва същия код в действителния отговор [2, §3.1.2]
detailОбяснение на това конкретно проявление; потребителите не бива да го анализират, за да извличат информация [2, §3.1.4]
instanceИдентификатор на това проявление, за да може да се проследи [2, §3.1.5]

Ако няма type, приема се about:blank, което означава, че проблемът няма друга семантика освен тази на HTTP кода [2, §3.1.1 и §4.2.1]. Клиентите трябва да пренебрегват членовете на разширение, които не познават [2, §3.2], което позволява да се добавят собствени полета (например списък с невалидните полета), без да се счупи никой.

Онова, което е ценно за бизнеса, е в две правила. Първо: програмата решава по type, не по фразата. Ако фразата в detail се промени от «опитайте отново» на «опитайте пак по-късно», поведението на клиента не бива да се промени. Второ: status в тялото и кодът на първия ред трябва да съвпадат; самият стандарт предупреждава, че дублирането на информацията отваря възможност двете да разминат [2, §5]. Грешка със status: 404 в тялото върху отговор 200 е случаят отгоре, облечен в стандартен формат.

При стабилен type (и обикновено стабилен title) един и същи проблем произвежда един и същи идентификатор; detail, instance и разширенията могат да варират при всяко проявление. Създателят на отговора е този, който трябва да направи status да съвпада с кода; един посредник може да го промени после. Това позволява да се броят проявленията по вид на таблото, а не по текст.

Би ли могла организацията Ви днес да преброи колко пъти е настъпил един и същи вид грешка за седмица, без да чете съобщенията едно по едно?

Повторни опити: какво се повтаря и какво не

Изборът на код е половината от работата. Другата половина е какво прави клиентът с него. Основното правило идва от стандарта за методите: една заявка е идемпотентна, ако повторението ѝ дава същия ефект като еднократното ѝ изпълнение. PUT, DELETE и безопасните методи, като GET, са такива; POST не е [1, §9.2.2]. И стандартът сочи, че клиентът не бива автоматично да повтаря неидемпотентен метод, освен ако няма начин да знае, че в този случай семантиката е идемпотентна, или да установи, че първоначалната заявка никога не е била приложена [1, §9.2.2].

Приложено към контакта:

Повторни опити: какво се повтаря и какво не
ЗаявкаHTTP свойство на методаПоследица от грешката
GET /contactos/{id}Безопасен и идемпотентенДопълнително натоварване
PUT /contactos/{id} с пълния контактИдемпотентенСъщият преднамерен ефект
DELETE /contactos/{id}ИдемпотентенВтория път може да отговори 404/410 и това е правилно
POST /contactos (създаване)Неидемпотентен по определениеДва еднакви контакта, освен ако договорът не го предотвратява

За да стане създаването безопасно, има разпространена практика: клиентът изпраща ключ за идемпотентност в заглавие Idempotency-Key, така че сървърът да разпознае повторния опит. Едно предупреждение: заглавието е описано в чернова на IETF (ревизия 07), която към 20 октомври 2026 г. фигурира като изтекла (18 април 2026 г.) и не е публикуван стандарт [4]; използвайте го като препратка, не като норма. Ако договорът Ви възприеме шаблона, документирайте срока за съхранение на ключовете и на отговорите. Какво предлага черновата: същият ключ със същата заявка връща първоначалния резултат; същият ключ с друго съдържание — 422; а повторение, докато първоначалната заявка още се изпълнява — 409 [4].

Безопасността на повторния опит зависи и от договора, от страничните ефекти и от неопределеността на мрежата, не само от метода. На тази основа клиентът може да има таблица за решение по код. Коментирана версия за нашия случай:

Повторни опити: какво се повтаря и какво не
ОтговорДействие на клиентаЗащо
200, 201, 204ПродължаваМинало е добре
400, 422Не повтаря; поправя или показваПовторението в същия вид дава същото
401Подновява данните веднъж; ако се върне, спираЦикълът на подновяване нищо не решава
403Не повтаря; ескалираИма политика, която го отрича
404, 410Не повтаря; приема «не съществува» за резултатТова е информация, не неизправност
409, 412Чете наново състоянието, решава, изпраща отновоСъстоянието се е променило, откакто е било прочетено
429Изчаква Retry-After и се връщаУважава капацитета на сървъра
502, 504 при записПроверява състоянието, преди да повториМоже да е било приложено
500, 503Само ако методът е идемпотентен или договорът предлага проверима идемпотентност: повтаря с нарастващо, случайно по продължителност изчакване и с таван, като уважава Retry-After, ако идва; иначе проверява състояниетоВероятно временна неизправност, но записът може да е бил приложен
Непознат кодТретира го като x00 на неговото семейство [1, §15]Правило на стандарта

Редът за 500 и 503 изисква нюанс: нарастващото изчакване със случаен компонент и таван е обичайна инженерна практика, не изискване на стандарта. Целта му е хиляда клиенти да не се върнат всички в една и съща секунда и да съборят отново услуга, която се е възстановявала.

Ако Вашият доставчик отговаря с 503 в продължение на петнайсет минути, какво правят интеграциите Ви: чакат подредено, настояват безспир или спират без да предупредят?

Колко струва грешката: три сценария

Следващото са илюстративни сценарии, без цифри; служат да се види механизмът на вредата.

1. Интеграцията, която повтаря онова, което не бива. Приложение за продажби създава контакти във Вашето API. В един ден базата данни се забавя и API връща 504 при някои създавания. Кодът на приложението третира всяко 5xx като «повторете» и някои от създаванията вече са били приложени. Резултат: дублирани контакти, кампании, които пишат два пъти на един и същ клиент, и работа по отстраняване, която никой не е планирал. Ако клиентът, сървърът и договорът прилагат политиката «проверете състоянието, преди да повторите» и ключ за идемпотентност при създаването, същият инцидент има много по-малка вероятност да остави дубликати.

2. Таблото, което изглежда зелено. Един екип измерва здравето на своето API по дела на отговорите 5xx. API връща 200 с грешка в тялото, когато CRM не отговаря. Таблото показва зелено, докато каналите не могат да запитват контакти. В обратния смисъл също има цена: ако всяко търсене без резултат се връща като 404, потребителите на API натрупват «грешки», които не са такива, и дежурният екип се научава да пренебрегва сигнала. Полезното табло изисква кодът да означава нещо: 4xx за онова, което поправя извикващият, 5xx за онова, което поправя екипът, отговарящ за услугата.

3. Агентът, който не знае какво да прави. Все повече API се ползват от програма, която решава сама, включително асистенти и агенти с изкуствен интелект, които извикват инструменти. Агент, който получи 200 с фразата «не можах да го намеря», трябва да тълкува естествен език, за да разбере дали е имало неуспех. Този, който получи 404 със стабилен type, знае, че може да съобщи «не съществува». Който получи 429 с Retry-After, знае колко да чака; който получи 503 без никакво указание, може да настоява или да се откаже, според как е програмиран. detail в свободен език не бива да се анализира, за да се вземат решения [2, §3.1.4]; type — да. Добре избраният код и вид намаляват неяснотата, която агентът трябва да овладее. Същата тема, погледната от гледна точка на онова, което системата Ви трябва да има, за да я ползват агентите, разглеждаме в «API, готови за ИИ агенти».

Кой от тези три сценария би могъл да се случва днес в някоя от интеграциите Ви, без екипът Ви да го знае?

Цялостен преглед: контактът от край до край

За да затворим примера, ето последователност с отговорите, които би дало добре проектирано API. Всеки ред е хипотетично извикване спрямо договора по-горе.

Цялостен преглед: контактът от край до край
#ИзвикванеОтговорПрочит
1GET /contactos?correo=ana@ejemplo.com (съществува)200, списък с един контактУспех на справката
2GET /contactos?correo=nadie@ejemplo.com200, празен списъкТърсенето е сработило; няма съвпадения
3GET /contactos/c-0001 (съществува)200, контактът, с ETagУспех с версия
4GET /contactos/c-9999 (никога не е съществувал)404, Problem DetailsРезултат, не неизправност
5GET /contactos/c-0002 (слят с друг)410, Problem Details със заместникаДа спре да го иска
6GET /contactos/%%%400Нечетима заявка
7POST /contactos с имейл без «маймунско ухо»422Данни, които не отговарят на правилото
8POST /contactos с вече регистриран имейл409Сблъсък с онова, което е записано
9PUT /contactos/c-0001 с изтекъл If-Match412Някой е променил преди това
10Поредица от търсения, която надхвърля договорения лимит429 с Retry-AfterПревишено темпо
11GET /contactos/c-0001 при спрял CRM503 с Retry-AfterВърнете се след малко
12POST /contactos и CRM не отговаря навреме504Проверете, преди да повторите

Дванайсет реда и десет различни кода. Редове 11 и 12 са възможна политика на договора, за API, което действа като посредник. Документираната и проверена таблица на договора намалява неяснотата между екипите и превръща устното споразумение в нещо, което може да се провери.

Съществува ли днес, за най-използваните Ви API, такава таблица —сценарий, код, прочит— или всеки интегратор я възстановява чрез проби и грешки?

Шест въпроса за следващия преглед на Вашите API

За носене на следващата среща по архитектура, без да е нужно да се отваря код:

  1. Излиза ли някоя грешка с 200? Поискайте извадка от отговори с грешка и погледнете първия ред.
  2. Има ли единна политика за «не е намерено»? Различава ли политиката търсене без съвпадения (200), несъществуващ ресурс (404) и премахнат ресурс (410) и прилага ли се последователно към равностойни случаи във всички маршрути?
  3. Различават ли се 400, 422 и 409? Ако всичко е 400, екипите Ви за поддръжка плащат разликата.
  4. Казват ли 429 и 503 колко да се чака? Retry-After е по желание; когато липсва, всеки клиент решава сам, така че е добре да се документира политиката.
  5. Имат ли грешките общ формат и стабилен type? Това позволява на табло, на партньор и на агент да решават, без да четат проза.
  6. Могат ли създаванията да се повтарят без дублиране? Ако не, всяко 504 е риск от двойни данни.

Много решения започват в договора; реализацията им може да изисква промени в приложението или в платформата (ограничения на темпото, съхраняване на ключовете за идемпотентност, наблюдаемост).

С колко от шестте въпроса бихте се чувствали спокойни днес пред одит или пред партньор, който интегрира Вашето API?

Какво прави Hábil

Hábil работи в пространството между системите, които вече функционират във Вашата компания, и новите слоеве, които се нуждаят от тях: канали, партньори, асистенти и агенти. Част от тази тема —всеки отговор на едно API да казва ясно какво се е случило и какво да се направи— е онова, което кара една интеграция да работи, когато нещо се обърка, и е част от онова, което предлагаме в страницата Middleware и платформи за API. За да ползва асистент или агент с изкуствен интелект системите Ви с мярка и с ясни граници, вижте страницата Изкуствен интелект за предприятия (Enterprise AI).

Първият разговор, безплатен, тръгва от картата на интеграциите Ви и от точките, в които един двусмислен отговор би могъл да Ви струва пари, време или доказателства. Пишете ни в WhatsApp.

Разгледани на 20 октомври 2026 г. (текстовете на RFC са прочетени в официалната им версия от RFC Editor и цитираните откъси са сверени).

Източници

  1. IETF, RFC 9110, HTTP Semantics (юни 2022). Раздели: §9.2.2 идемпотентни методи; §10.2.3 Retry-After; §15 и §15.1 класове кодове, препоръчани фрази, непознат код, третиран като x00, евристично кешируеми кодове; §15.3.1 200; §15.5.1 400; §15.5.2 401; §15.5.4 403; §15.5.5 404; §15.5.6 405; §15.5.10 409; §15.5.11 410; §15.5.13 412; §15.5.21 422; §15.6.1 500; §15.6.3 502; §15.6.4 503; §15.6.5 504. https://www.rfc-editor.org/rfc/rfc9110.html
  2. IETF, RFC 9457, Problem Details for HTTP APIs (заменя RFC 7807). §3 и §3.1 членове и медиен тип application/problem+json; §3.1.1 type; §3.1.2 status; §3.1.4 detail; §3.2 разширения; §4.2.1 about:blank; §5 съображения за сигурността. https://www.rfc-editor.org/rfc/rfc9457.html
  3. IETF, RFC 6585, Additional HTTP Status Codes, §3 (428 Precondition Required) и §4 (429 Too Many Requests, Retry-After, без съхраняване в кеш). https://www.rfc-editor.org/rfc/rfc6585.html
  4. IETF HTTPAPI, The Idempotency-Key HTTP Header Field (Internet-Draft draft-ietf-httpapi-idempotency-key-header, ревизия 07; чернова, не публикуван стандарт; изтичане, регистрирано на 18 април 2026 г. в Datatracker). https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/