Каким типом ошибки становится каждый код
PHPТип ошибки, который каждый SDK выбрасывает для кода ошибки API, как типы связаны между собой и какие ошибки SDK выбрасывают сами.
Каждый SDK выбрасывает для каждого кода ошибки API свой тип, и оба SDK в этом совпадают: код соответствует одному и тому же типу в PHP и в Node.js. Эта страница называет тип каждого кода и ошибки, которые SDK выбрасывают без ответа API.
Как связаны типы
У всех ошибок SDK один корневой тип:
SlenExceptionв PHP, в пространстве имёнSlen\Sdk\Exceptions, иSlenErrorв Node.js.catchкорневого типа ловит любую ошибку, которую выбрасывает SDK.Тип называется одинаково в обоих языках:
BudgetExceededExceptionв PHP — этоBudgetExceededErrorв Node.js.У отказов в трате свой родитель:
SpendRefusedExceptionв PHP иSpendRefusedErrorв Node.js. Каждый ответ со статусом402выбрасывается как этот тип или как тип, который его расширяет.Заблокированный клиент,
CustomerBlockedExceptionилиCustomerBlockedError, расширяет тип отказа политики,PolicyDeniedExceptionилиPolicyDeniedError.Каждый другой тип расширяет корневой напрямую.
Код, для которого у SDK нет типа, выбрасывается по своему статусу: как ошибка ключа для
401или403, как отказ в трате для402, как лимит запросов для429, как ошибка провайдера для5xxи как отклонённый запрос для любого другого статуса.
Тип каждого кода
Таблица не включает коды консоли и настроек: ни один вызов SDK не делает запросов, на которые отвечают этими кодами.
| Код | HTTP-статус | Тип ошибки в SDK | Что это значит | Что делать |
|---|---|---|---|---|
invalid_api_key | 401 | AuthenticationException PHPAuthenticationError Node.js | В запросе нет API-ключа или есть ключ, которого Slen не знает, — скопированный с ошибкой, отозванный или заменённый ротацией. | Проверьте значение заголовка Authorization. Отозванный ключ больше не работает — выпустите новый в консоли. |
insufficient_scope | 403 | AuthenticationException PHPAuthenticationError Node.js | Ключ действителен, но у него нет разрешения, которого требует операция, — или его не включает роль человека, вошедшего в консоль. Сообщение ошибки называет это разрешение и в обоих случаях говорит об API-ключе. | Используйте ключ с этим разрешением или выпустите такой ключ. Давайте ключу только те разрешения, которые нужны его сервису. Человеку, вошедшему в консоль, нужна роль, которая включает это разрешение; её даёт владелец или администратор организации. |
not_authenticated | 401 | AuthenticationException PHPAuthenticationError Node.js | Никто не вошёл. В запросе нет ни API-ключа, ни сессии человека, вошедшего в консоль, или сессия закончилась. | Отправьте API-ключ в заголовке Authorization или снова войдите в консоль. |
not_a_member | 403 | AuthenticationException PHPAuthenticationError Node.js | Человек, вошедший в консоль, не участник организации, о которой запрос, или ещё не состоит ни в одной организации. | Примите приглашение в организацию или переключитесь на организацию, участником которой вы являетесь. |
project_access_restricted | 403 | — | Вызывающий видит только часть проектов организации и попытался изменить то, что принадлежит всей организации, или прочитать журнал аудита. API-ключу доступен только проект, которому он принадлежит. | С API-ключом создавайте нужное в проекте ключа — подключение провайдера, у которого scope равен project. Человек просит внести изменение участника, которому доступны все проекты. |
account_suspended | 403 | AuthenticationException PHPAuthenticationError Node.js | Учётная запись человека, который входит, приостановлена, и вход отклонён. | Учётная запись не может войти, пока её не восстановят. Напишите в поддержку, чтобы узнать причину. |
policy_denied | 403 | PolicyDeniedException PHPPolicyDeniedError Node.js | Политика отклонила запрос до того, как были зарезервированы какие-либо деньги. Сообщение ошибки называет политику. | Прочитайте в сообщении, какая политика отклонила запрос. Измените запрос — модель, операцию, стоимость — или измените политику в консоли. |
model_not_allowed | 403 | PolicyDeniedException PHPPolicyDeniedError Node.js | Узел модели во флоу называет модель, которой нет в каталоге, или модель, которая выключена. Выполнение заканчивается как failed с этим кодом. | Выберите в узле модели модель из списка моделей — в нём активные модели, у которых есть действующая цена, — и опубликуйте новую версию. |
budget_exceeded | 402 | BudgetExceededException PHPBudgetExceededError Node.js | Вызов вывел бы бюджет за его лимит. То, что бюджет уже держит и потратил в текущем окне, вместе с холдом, который просит этот вызов, больше лимита. | Дождитесь следующего окна бюджета — его resets_at говорит когда; бюджет типа total заново не начинается. Или поднимите лимит бюджета. |
operation_limit_exceeded | 402 | OperationLimitExceededException PHPOperationLimitExceededError Node.js | Бюджет на операцию ограничивает, сколько может зарезервировать один вызов операции, а холд, который просит этот вызов, выше этого ограничения. | Просите меньше — меньше выходных токенов, модель дешевле — или поднимите ограничение. Тот же запрос будет отклонён снова, пока одно из них не изменится. |
concurrency_limit_exceeded | 402 | ConcurrencyLimitException PHPConcurrencyLimitError Node.js | Бюджет типа concurrency — в консоли «Одновременные резервы» — разрешает держать открытыми заданное число холдов одновременно, и все они заняты вызовами, которые выполняются. | Отправьте запрос снова, когда закончится один из выполняющихся вызовов, или поднимите лимит бюджета. Запрос к флоу отправляйте снова с новым ключом, потому что его отклонённое выполнение сохраняет ключ, который у него был. Это не лимит запросов ключа. |
insufficient_credits | 402 | InsufficientCreditsException PHPInsufficientCreditsError Node.js | Счёт клиента не может покрыть цену вызова в кредитах, или у клиента нет счёта этого типа кредитов. Списание или корректировка отклоняются так же, когда оставили бы баланс ниже того, что держит счёт. | Пополните кредиты клиента. Счёт, в настройках которого разрешён отрицательный баланс, не отклоняет. |
customer_blocked | 403 | CustomerBlockedException PHPCustomerBlockedError Node.js | Клиент, для которого сделан запуск, заблокирован. Новый запуск этого клиента отклоняется до того, как что-либо создано. Запуск, который уже идёт, отклоняется на следующем холде кредитов, и выполнение завершается с этим кодом. | Разблокируйте клиента, чтобы его запуски снова проходили. Пока клиент заблокирован, его кредиты можно пополнять, списывать и корректировать, а выписку — читать. |
customer_has_history | 409 | — | Клиента нельзя удалить, потому что с ним уже что-то происходило — на счёте есть кредиты или резерв, или в выписке есть запись. Журнал кредитов только дополняется, и его записи объясняют баланс. | Заблокируйте клиента вместо удаления. |
customer_not_found | 404 | CustomerNotFoundException PHPCustomerNotFoundError Node.js | Проект не знает клиента, которого называет запрос, — клиента не добавляли, не пополняли и не настраивали, или его удалили. Так отклоняется вызов операции с ценой для такого клиента, а также чтение, блокировка, списание и удаление. Корректировка счёта, которого нет, отклоняется так же. | Сначала добавьте клиента или пополните его — пополнение создаёт клиента и счёт. |
overrun_stopped | 402 | OverrunStoppedException PHPOverrunStoppedError Node.js | Оценка вызова модели во флоу, сделанная заново после того, как взят холд, и до вызова провайдера, превысила холд больше, чем позволяет допуск превышения узла, а политика перерасхода узла модели — остановить. Холд возвращён, провайдер не вызывался. Тот же код завершает выполнение, когда человек отклонил согласование, которое запросил перерасход, или никто не ответил вовремя. У нового узла модели политики перерасхода нет, а политика, которая у узла есть, практически не срабатывает, потому что обе оценки считаются по одному и тому же запросу с разницей в мгновение. | Отправьте запрос снова с новым ключом — вызов будет оценён заново и возьмёт собственный холд. Чтобы таких отказов не было, выключите политику перерасхода узла модели и опубликуйте новую версию. |
overrun_approval_required | 402 | OverrunApprovalRequiredException PHPOverrunApprovalRequiredError Node.js | Оценка вызова модели во флоу, сделанная заново после того, как взят холд, и до вызова провайдера, превысила холд больше, чем позволяет допуск превышения узла, а политика перерасхода узла модели просит человека одобрить разницу. Выполнение ждёт этого решения, поэтому на запрос отвечает ждущее выполнение, а не этот код. У нового узла модели политики перерасхода нет, а политика, которая у узла есть, практически не срабатывает, потому что обе оценки считаются по одному и тому же запросу с разницей в мгновение. | Примите решение по согласованию в консоли. Согласование, которое отклонили или по которому никто не решил вовремя, завершает выполнение с кодом overrun_stopped. |
reservation_not_found | 404 | — | Это не ответ ни одной операции API. Slen держит этот код для холда, который не может найти, пока закрывает вызов модели. | Обрабатывать нечего. Если ответ всё же его принесёт, напишите в поддержку и приведите id запроса. |
reservation_expired | 409 | — | Это не ответ ни одной операции API. Slen держит этот код для продления истёкшего холда; истёкший холд всё ещё принимает списание своего вызова. | Обрабатывать нечего. Если ответ всё же его принесёт, напишите в поддержку и приведите id запроса. |
invalid_request | 409, 422 | — | Запрос нельзя выполнить в таком виде, и сообщение ошибки говорит почему — политика без параметра, который нужен её действию, или с условием, которое её действие никогда не встречает; выбранные проекты для участника, чья роль открывает все проекты; заголовок Idempotency-Key длиннее 190 символов. SDK сами выбрасывают ошибку с этим кодом для вызова, который отклоняют до отправки. | Прочитайте сообщение и измените запрос. |
idempotency_key_required | 400 | — | Операция требует заголовок Idempotency-Key, а в запросе его нет или он пустой. | Отправьте собственный ключ, который называет единицу работы, и отправляйте тот же ключ, когда повторяете эту работу. |
idempotency_conflict | 409 | IdempotencyConflictException PHPIdempotencyConflictError Node.js | Ключ Idempotency-Key уже использовал другой запрос — тот же ключ с другим телом или ключ другого пополнения, списания или корректировки проекта. | Отправьте новый запрос с новым ключом. На повтор первого запроса с его ключом отвечают как прежде. |
idempotency_in_progress | 409 | IdempotencyConflictException PHPIdempotencyConflictError Node.js | Вызов модели во флоу встретил холд, который всё ещё стоит под его собственным ключом, и был отклонён, чтобы провайдер не был вызван дважды. Выполнение заканчивается как failed с этим кодом. Обычный запуск с этим не встречается. | Прочитайте выполнение и его трассировку. Отправляйте запрос снова с новым ключом, только если ответ всё ещё нужен, — за первый вызов могли заплатить. |
cost_calculation_failed | 422 | InvalidRequestException PHPInvalidRequestError Node.js | Модель активна, но у неё нет действующей цены, поэтому Slen не может посчитать, сколько будет стоить вызов, и вызов отклоняется. | Назовите модель из списка моделей — в нём активные модели, у которых есть действующая цена. |
provider_not_configured | 422 | ProviderException PHPProviderError Node.js | Ни одно подключение проекта не может обслужить провайдера запрошенной модели, поэтому вызов некуда отправить. Во флоу тот же код приходит, когда пул, через который узел модели направляет вызов, удалён или выключен. | Добавьте подключение к этому провайдеру в консоли или включите то, которое есть. Включите пул снова или выберите в узле модели другой пул и опубликуйте новую версию. Затем отправьте запрос с новым ключом. |
provider_rate_limit | 429 | ProviderException PHPProviderError Node.js | Провайдер ответил, что его лимит запросов исчерпан. Пул, который переключает подключения при лимите запросов, сначала пробует другое; этот код приходит, когда ничего другого не пробовали. | Отправьте вызов снова позже с новым ключом. Добавьте подключение в пул или разрешите пулу переключать подключения при лимите запросов. |
provider_error | 502 | ProviderException PHPProviderError Node.js | Провайдер отклонил вызов или ответил тем, чем Slen не может воспользоваться, и вызов не завершился. | Проверьте, чего запрос просит у модели, — параметр, которого модель не принимает, провайдер отклоняет. Отправьте вызов снова с новым ключом, когда запрос будет верным. |
provider_timeout | 504 | ProviderException PHPProviderError Node.js | До провайдера не удалось достучаться — имя не разрешилось или соединение не установилось, — поэтому вызов не ушёл. Вопреки названию, это не значит, что модель отвечала медленно. | Отправьте вызов снова позже с новым ключом. Провайдер, до которого достучались и который не ответил вовремя, — это operation_uncertain, а не этот код. |
provider_unavailable | 502 | ProviderException PHPProviderError Node.js | Провайдер ответил сбоем на своей стороне. Slen ставит это подключение на паузу, а пул, который переключает подключения, пробует другое. | Отправьте вызов снова позже с новым ключом. Пул с несколькими подключениями переживает сбой одного из них. |
pool_exhausted | 503 | ProviderException PHPProviderError Node.js | Ни одно подключение пула не смогло обслужить вызов за то число попыток, которое разрешает пул. last_error_code в деталях ошибки называет последний сбой, а заголовок Retry-After говорит, сколько ждать, когда это известно. | Подождите столько, сколько говорит Retry-After, и отправьте вызов снова с новым ключом. Если пул исчерпывается часто, добавьте в него подключения. |
operation_uncertain | 504 | OperationUncertainException PHPOperationUncertainError Node.js | Провайдер был вызван, а его ответ не вернулся, поэтому неизвестно, был ли вызов сделан и оплачен. Так же заканчивается вызов модели во флоу, который шёл в фоне, если провайдер его принял, а спросить провайдера о нём больше не удалось. Холд не возвращается — он держится, пока не истечёт. Выполнение, которое так закончилось, получает статус uncertain. | Не отправляйте вызов снова с новым ключом — это может быть второй оплаченный вызов. Прочитайте операцию или выполнение, чтобы узнать, что произошло. Событие вебхука operation.uncertain тоже сообщает о таком вызове. |
ledger_unavailable | 503 | ServiceUnavailableException PHPServiceUnavailableError Node.js | Реестр трат был недоступен, когда вызов попросил свой холд. Вызов не авторизован — для него ничего не зарезервировано, и его провайдер не вызывался. | Отправьте запрос снова позже — запрос к флоу с новым ключом, потому что его неудавшееся выполнение сохраняет ключ, который у него был. GET /v1/degraded-status говорит, что API пропускает, пока реестр недоступен. |
flow_runtime_error | — | FlowRuntimeException PHPFlowRuntimeError Node.js | Код, который среда выполнения держит для флоу, сломавшегося во время выполнения по причине, для которой у неё нет более узкого кода. Сейчас этим кодом не отвечает ни один запрос и им не заканчивается ни одно выполнение — у сбоя выполнения есть более узкий код или нет никакого. | Сейчас обрабатывать нечего. Если ответ всё же принесёт этот код, прочитайте трассировку выполнения, чтобы увидеть, где оно остановилось. Вызовы моделей до этого места оплачены, и повтор с новым ключом оплатит их снова. |
invalid_flow_input | 422 | — | Во входе нет того, что нужно узлу флоу, — флоу, который вызывает модель, требует непустой messages во входе. Выполнение закрывается как failed, и ключ остаётся за ним. | Исправьте вход и отправьте его с новым ключом. На повтор с тем же ключом отвечает неудавшееся выполнение. |
step_limit_exceeded | 500 | FlowRuntimeException PHPFlowRuntimeError Node.js | Выполнение не дошло до конечного узла за то число шагов, которое разрешено одному выполнению. Флоу больше, чем среда выполнения проходит за один запрос. | Сократите флоу или разделите работу между двумя операциями. Затем отправьте запрос с новым ключом — на повтор с тем же ключом отвечает неудавшееся выполнение. |
unrouted_port | 500 | FlowRuntimeException PHPFlowRuntimeError Node.js | Узел вышел через порт, к которому ничего не подключено, и выполнению некуда было идти. Сообщение ошибки называет узел и порт. | Подключите этот порт в конструкторе флоу и опубликуйте новую версию. Затем отправьте запрос с новым ключом — на повтор с тем же ключом отвечает неудавшееся выполнение. |
execution_timeout | 504 | TimeoutException PHPTimeoutError Node.js | Выполнение флоу исчерпало время, которое ему отведено. Вызов модели, который ответил до этого, оплачен. | Прочитайте трассировку выполнения, чтобы увидеть, куда ушло время. Прежде чем повторять запрос, учтите, что повтор с новым ключом снова оплатит то, что уже оплачено. |
execution_parked | — | — | Это не ответ API. Внутри среды выполнения он отмечает выполнение, которое остановилось, чтобы ждать, а на запрос отвечает само выполнение — ждущее или в очереди. | Обрабатывать как ошибку нечего. Дождитесь выполнения так же, как любого другого, которое ждёт. |
http_request_failed | 502 | FlowRuntimeException PHPFlowRuntimeError Node.js | Вызов, который флоу делает к другому HTTP API, не удался или получил в ответ ошибку, а к порту ошибки узла, который его сделал, ничего не подключено. Это не провайдер модели. Этот узел заморожен, если сервис не настроен иначе, — уже опубликованная с ним версия выполняется, а новая версия содержать его не может. | Проверьте API, которое вызывает флоу, затем отправьте запрос с новым ключом — на повтор с тем же ключом отвечает неудавшееся выполнение. Порт ошибки замороженного узла нельзя подключить в новой версии — чтобы изменить, что флоу делает при такой ошибке, опубликуйте версию без этого узла. |
rate_limit_exceeded | 429 | RateLimitException PHPRateLimitError Node.js | API-ключ или человек, вошедший в консоль, отправил за минуту больше запросов, чем позволяет лимит. У нескольких операций свой, меньший лимит — например, у проверки подключения провайдера. Заголовок Retry-After ответа говорит, сколько секунд ждать. Чтение статуса выполнения тоже считается. | Подождите столько, сколько говорит Retry-After, и отправьте запрос снова. Для отклонённого запроса ничего не зарезервировано и не потрачено. |
flow_not_found | 404 | — | На код операции не отвечает ни один флоу — к нему не привязан флоу, у флоу нет опубликованной версии или флоу в архиве. Ничего не создаётся, поэтому ключ идемпотентности остаётся свободным. Тот же код отвечает на id флоу, который вызывающему недоступен. | Привяжите код операции к флоу и опубликуйте флоу. Повтор с тем же ключом выполнится, когда флоу сможет ответить. |
operation_not_found | 404 | — | Ни у одной операции — записи одного вызова модели с его попытками у провайдеров — нет такого id в проекте ключа. | Проверьте id. Операция другого проекта отвечает так же. |
invalid_credentials | 401, 502 | — | Два случая. Вход в консоль, при котором почта и пароль не совпадают ни с одной учётной записью. Вызов модели, при котором провайдер отклонил ключ подключения, обслуживавшего вызов, — тогда Slen отмечает учётные данные этого подключения как недействительные. | Войдите с правильным паролем. Для подключения укажите в нём действующий ключ провайдера в консоли. |
csrf_token_mismatch | 419 | — | Запрос сессии консоли пришёл без своего CSRF-токена или с устаревшим. Запрос с API-ключом этот код никогда не получает. | Перезагрузите страницу консоли и повторите действие. |
service_unavailable | 503 | ServiceUnavailableException PHPServiceUnavailableError Node.js | Slen не смог достучаться до своей базы данных и ответил, ничего не сделав. Тот же код отвечает на изменение бюджета, которое слишком долго ждало вызовов, выполняющихся на этом бюджете; ничего не изменено. | Отправьте запрос снова позже. Отклонённое изменение бюджета говорит в Retry-After, когда. |
not_found | 404 | — | Регистрация и вход в консоль ответили так, будто такого адреса нет. Они открыты только для браузера, в котором открыли выданную ссылку доступа. | Откройте свою ссылку доступа в этом браузере, затем снова зарегистрируйтесь или войдите. |
background_hold_expired | — | TimeoutException PHPTimeoutError Node.js | Это не ответ API — им заканчивается выполнение, запущенное в асинхронном режиме. Вызов модели во флоу выполнялся у провайдера в фоне, и его холд был закрыт другим списанием, пока вызов ждал, поэтому вызов закончился, не спросив у провайдера ответ. Ничего не возвращено, и провайдер всё ещё может выставить счёт за принятый вызов. Вопреки названию, холд, который просто истёк, так вызов не заканчивает. | Отправляйте запрос снова с новым ключом, только если ответ всё ещё нужен. Провайдер может выставить счёт и за первый вызов, поэтому за ответ, возможно, придётся заплатить дважды. |
execution_conflict | 500 | FlowRuntimeException PHPFlowRuntimeError Node.js | Выполнение не удалось открыть — его запись столкнулась с другой, и не по ключу идемпотентности. Флоу не выполнялся. | Отправьте запрос снова. Если это повторится, напишите в поддержку и укажите id запроса. |
unknown_node | 500 | FlowRuntimeException PHPFlowRuntimeError Node.js | Опубликованная версия флоу ведёт к узлу, которого среда выполнения не может найти, поэтому выполнение остановилось. | Опубликуйте флоу заново из конструктора флоу, затем отправьте запрос с новым ключом — на повтор с тем же ключом отвечает неудавшееся выполнение. Если с новым ключом ошибка повторится, напишите в поддержку и укажите id запроса. |
node_type_not_supported | 500 | FlowRuntimeException PHPFlowRuntimeError Node.js | В опубликованной версии флоу есть узел типа, который среда выполнения не умеет выполнять. | Замените узел в конструкторе флоу и опубликуйте новую версию. Затем отправьте запрос с новым ключом — на повтор с тем же ключом отвечает неудавшееся выполнение. |
flow_execution_not_found | 404 | — | Выполнения с таким id нет в проектах, доступных вызывающему, — его не было вовсе, или оно принадлежит другому проекту. | Проверьте id. Это execution_id ответа, который начал выполнение. |
Для кода без собственного типа SDK выбирает тип ошибки по HTTP-статусу ответа:
401,403AuthenticationExceptionPHP,AuthenticationErrorNode.js402SpendRefusedExceptionPHP,SpendRefusedErrorNode.js429RateLimitExceptionPHP,RateLimitErrorNode.js- 500 и выше
ProviderExceptionPHP,ProviderErrorNode.js - любой другой статус
InvalidRequestExceptionPHP,InvalidRequestErrorNode.js
Ошибки, которые SDK выбрасывают сами
Некоторые ошибки приходят без ответа API или до отправки запроса.
| Тип | Когда |
|---|---|
ConnectionException, ConnectionError | До Slen не удалось достучаться, или соединение оборвалось, прежде чем ответ прочитан |
TimeoutException, TimeoutError | Ответ не пришёл за таймаут SDK — timeout и connectTimeout у SlenConfig, timeoutMs в Node.js — или за таймаут вызова |
InvalidRequestException, InvalidRequestError | SDK отклоняет вызов до отправки: пустой ключ идемпотентности, пустой внешний идентификатор, сумма кредитов, которая не десятичная строка, контекст, который называет другого клиента, таймаут или число повторов вызова вне допустимого. Код — invalid_request |
InvalidResponseException, InvalidResponseError | API ответил 2xx с телом, которое нельзя прочитать; ошибка хранит статус. Для доставки вебхука, подпись которой сошлась, а тело — не событие, выбрасывается тот же тип |
WebhookSignatureException, WebhookSignatureError | Подпись доставки вебхука не сходится — Вебхуки |
У ошибки соединения и у таймаута нет ни кода, ни статуса. У каждой другой ошибки этой таблицы есть код: собственный код SDK, которым API никогда не отвечает, — кроме invalid_request, которым пользуется и API.
Собственные коды SDK и тип ошибки, который для каждого выбрасывает каждый SDK:
| Код | PHP | Node.js |
|---|---|---|
invalid_request | InvalidRequestException | InvalidRequestError |
invalid_response | InvalidResponseException | InvalidResponseError |
webhook_signature_invalid | WebhookSignatureException | WebhookSignatureError |
webhook_payload_invalid | InvalidResponseException | InvalidResponseError |
После ошибки соединения или таймаута неизвестно, сделал ли Slen работу. После нечитаемого ответа тоже неизвестно: вызов мог пройти.
Настройка, с которой SDK нельзя собрать, — пустой ключ, таймаут не больше нуля — это InvalidArgumentException, выброшенный там, где собирают SDK. Это ошибка в коде, а не ошибка SDK, и она не расширяет SlenException.
Что читать дальше
Обработка ошибок — ловите ошибки по типу и узнавайте по типу, стоил ли запуск денег.
Когда трата отклонена — семейство отклонённой траты и две его формы.
Коды ошибок — что значит каждый код и что с ним делать.
Собрано из openapi b070e1f, sdk contract 6a2e0e0, php 4089db3, node 0fa87be