# POST Webhooks

Создание или обновление sandbox webhook endpoint.

## POST /api/sandbox/webhooks

> Создает или обновляет sandbox webhook endpoint для текущего API-ключа. URL должен быть публичным \`https\://\` без credentials и без local/private host.\
> \
> Первое создание и \`rotate\_secret=true\` возвращают \`signingSecret\`; сохраните его сразу. Для POST обязателен \`Idempotency-Key\`.

```json
{"openapi":"3.0.3","info":{"title":"Fundora API","version":"0.3.0"},"tags":[{"name":"post-webhooks","description":"Создание или обновление sandbox webhook endpoint."}],"servers":[{"url":"https://partner.fundora.capital","description":"Production-домен партнерского API"}],"security":[{"SandboxBearer":[]}],"components":{"securitySchemes":{"SandboxBearer":{"type":"http","scheme":"bearer","bearerFormat":"sk_sandbox"}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"description":"Обязательный уникальный ключ POST-операции, обычно UUID. Повтор того же POST с тем же endpoint-ом и телом вернет сохраненный результат с idempotency_replayed=true. Тот же ключ с другим телом или endpoint-ом вернет IDEMPOTENCY_KEY_CONFLICT. Отсутствующий ключ вернет IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","maxLength":255}}},"schemas":{"UpsertSandboxWebhookRequest":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Публичный HTTPS endpoint. Local/private hosts отклоняются."},"enabled":{"type":"boolean","default":true},"events":{"type":"array","items":{"$ref":"#/components/schemas/SandboxWebhookEventType"},"description":"Непустой список events. Если поле не передано при первом создании, endpoint подписывается на все events."},"rotate_secret":{"type":"boolean","default":false,"description":"Если true, API создаст новый signing_secret и вернет его в ответе."}},"required":["url"]},"SandboxWebhookEventType":{"type":"string","enum":["sandbox.loan_application.updated","sandbox.loan.issued","sandbox.loan.updated","sandbox.repayment.updated","sandbox.repayment.settled","sandbox.collateral.returned","sandbox.partner.updated","sandbox.webhook.test"],"description":"Тип webhook event."},"SandboxWebhooksResponse":{"type":"object","properties":{"request_id":{"type":"string"},"idempotency_replayed":{"type":"boolean"},"success":{"type":"boolean"},"webhook":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/SandboxWebhookEndpoint"}]},"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/SandboxWebhookDelivery"}},"signingSecret":{"type":"string","description":"Полный signing secret. Возвращается только при создании endpoint или rotate_secret=true."}},"required":["request_id","success","webhook","deliveries"]},"SandboxWebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"environment":{"type":"string","enum":["sandbox"]},"url":{"type":"string","format":"uri","description":"Публичный HTTPS endpoint партнера."},"enabled":{"type":"boolean"},"events":{"type":"array","items":{"$ref":"#/components/schemas/SandboxWebhookEventType"}},"secretPreview":{"type":"string","description":"Маскированный signing secret. Полный secret возвращается только при создании/ротации."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"disabledAt":{"type":"string","format":"date-time","nullable":true}},"required":["id","environment","url","enabled","events","secretPreview","createdAt","updatedAt","disabledAt"]},"SandboxWebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventId":{"type":"string","format":"uuid"},"eventType":{"$ref":"#/components/schemas/SandboxWebhookEventType"},"status":{"type":"string","enum":["pending","processing","delivered","retrying","failed"]},"attemptCount":{"type":"integer","minimum":0},"nextAttemptAt":{"type":"string","format":"date-time","nullable":true},"lastAttemptAt":{"type":"string","format":"date-time","nullable":true},"lastStatusCode":{"type":"integer","nullable":true},"lastError":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","eventId","eventType","status","attemptCount","createdAt","updatedAt"]},"ErrorResponse":{"type":"object","properties":{"request_id":{"type":"string","description":"Идентификатор запроса для поддержки и поиска в Logs."},"error_code":{"type":"string","description":"Формализованный код ошибки для интеграционной логики.","enum":["SANDBOX_API_KEY_REQUIRED","SANDBOX_API_KEY_INVALID","SANDBOX_API_KEY_DATABASE_REQUIRED","SANDBOX_SESSION_REQUIRED","SANDBOX_RATE_LIMIT_EXCEEDED","INVALID_REQUEST","INVALID_ACTION","INVALID_SCOPE","LOAN_NOT_FOUND","WALLET_NOT_FOUND","FLOW_CONFLICT","LIMIT_EXCEEDED","INSUFFICIENT_BALANCE","IDEMPOTENCY_KEY_REQUIRED","IDEMPOTENCY_KEY_CONFLICT","IDEMPOTENCY_KEY_IN_PROGRESS","DATABASE_UNAVAILABLE","INTERNAL_ERROR"]},"error":{"type":"string","description":"Человекочитаемое описание ошибки. Не используйте его для интеграционной логики, для этого есть error_code."}},"required":["request_id","error_code","error"]}},"responses":{"BadRequest":{"description":"Некорректный запрос: неверное тело, отсутствует Idempotency-Key для POST, неизвестное действие или невалидная сумма","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Sandbox API-ключ отсутствует или недействителен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Conflict":{"description":"Flow нельзя продолжить из текущего состояния, не хватает баланса/лимита или конфликтует Idempotency-Key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"TooManyRequests":{"description":"Превышен Sandbox API rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/api/sandbox/webhooks":{"post":{"operationId":"upsertSandboxWebhook","tags":["post-webhooks"],"summary":"POST /api/sandbox/webhooks","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertSandboxWebhookRequest"}}}},"responses":{"200":{"description":"Webhook endpoint создан или обновлен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxWebhooksResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"description":"Создает или обновляет sandbox webhook endpoint для текущего API-ключа. URL должен быть публичным `https://` без credentials и без local/private host.\n\nПервое создание и `rotate_secret=true` возвращают `signingSecret`; сохраните его сразу. Для POST обязателен `Idempotency-Key`."}}}}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.fundora.capital/api-sandbox/post-webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
