---
title: "Open an authorization to connect a payment provider"
method: POST
path: "/v1/companies/{company_id}/payment-connections/authorizations"
tags: ["CompanyPaymentConnections"]
---

# Open an authorization to connect a payment provider

`POST /v1/companies/{company_id}/payment-connections/authorizations`

## Request

- Base URL: `https://app.beel.es/api`
- URL: `https://app.beel.es/api/v1/companies/{company_id}/payment-connections/authorizations`
- Auth: HTTP bearer

Opens an authorization session so the holder of a company your account **manages**
can connect a payment provider (`stripe`), and returns the `authorization_url` where they
authorize it.

- **`return_url`:** once the holder authorizes, BeeL's callback finalizes the connection
  and redirects back to the `return_url` of your portal, if you supplied one, with the
  parameters described under `return_url`.
- **When the connection appears:** it is created only when the holder authorizes, so it
  does not appear in `GET /v1/companies/{company_id}/payment-connections` until then. It
  is sealed under the NIF in the path, so auto-invoicing issues under that NIF.
- **The NIF must be activated in the mode of your API key** (`beel_sk_test_*` → Test,
  `beel_sk_live_*` → Live); otherwise the request answers `400`
  `COMPANY_NOT_ACTIVATED_IN_ENVIRONMENT` and no `authorization_url` is issued, because
  without activation there is no invoice series or tax configuration to invoice with.
  Test and Live activations are independent — a NIF activated in one mode still needs
  activating in the other.
- **One provider account, one NIF:** a provider account (`acct_...`) can be connected to a
  single NIF across the whole platform. Authorizing the same provider account from a second
  NIF does not move it: the callback fails with
  `OAUTH_ACCOUNT_CONNECTED_TO_OTHER_COMPANY`, and the existing connection keeps invoicing
  under the NIF it was sealed with. To move it, first
  `DELETE /v1/companies/{company_id}/payment-connections/{connection_id}` on the NIF
  that holds it, then open a new authorization on the NIF you want it under.

## Headers

- `Idempotency-Key` string

## Request body

- InitiatePaymentConnectionRequest: The authorization to open: which provider it is for, and where to send the holder back.
  - `provider` string, required: Payment provider slug in **lowercase**. Currently only `stripe` (Stripe Connect) is operative; `woocommerce` and `shopify` are reserved for future providers. Any other value answers `422`.
  - `return_url` string: URL of your portal to redirect the account holder back to after the OAuth callback completes. Must be an absolute `https://` URL. On **success** BeeL appends `status=success`, `provider` (slug), `company_id`, `connection_id` and `account` (the provider account id, e.g. `acct_...`). On **error** it appends `status=error`, `provider` and `message`, always a stable uppercase error code: `OAUTH_STATE_INVALID` (the authorization is unknown, expired or already used), `OAUTH_TOKEN_EXCHANGE_FAILED` (the provider rejected the code exchange), `ACCESS_DENIED` (the account holder declined at the provider), `OAUTH_ACCOUNT_CONNECTED_TO_OTHER_COMPANY` (the provider account is already connected to another NIF; disconnect it there first), `PROVIDER_ERROR` (any other provider-reported failure) or `OAUTH_UNEXPECTED`. When omitted, the callback redirects to BeeL's default integrations screen. A `return_url` that is not an absolute `https://` URL with a host is rejected up front with `422` `PAYMENT_RETURN_URL_INVALID`, and no authorization is opened.

## Response `200`

Authorization URL generated

- InitiatePaymentConnectionResponse
  - `success` boolean
  - `data` object
    - `authorization_url` string, uri, required: URL where the NIF's holder authorizes the Stripe Connect connection.
  - `meta` ResponseMeta
    - `timestamp` string, date-time
    - `request_id` string

## Other responses

- `400`: The NIF is not activated in the mode of your API key (`COMPANY_NOT_ACTIVATED_IN_ENVIRONMENT`). Activate it in that mode and retry; no authorization is opened and no authorization URL is issued.
- `401`: Missing or invalid authentication. Like every other error, `message`/`detail` is localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English when the header is missing or asks for none of those.
- `403`: Your account does not own or manage this company (`ACTIVE_COMPANY_NOT_ACCESSIBLE`). No authorization is opened. `MANAGED_ACCOUNT_HOLDER_REQUIRED` — the company belongs to a managed account that has no holder yet (provisioned without a person), so there is nobody to attribute the self-billed invoices to. Have the holder claim the account first, then connect.
- `422`: Validation or business rule error. One code is specific to this operation: - `PROVIDER_NOT_SUPPORTED` — `provider` is not one BeeL connects to (today, only `stripe`). Nothing is opened. - `VALIDATION_ERROR` — the generic validation failure, with the offending field in `error.details`.
- `429`: Rate limit exceeded. `RATE_LIMIT_EXCEEDED` when the request rate of your credential is over its limit; `TOO_MANY_REQUESTS` when an email the operation sends is held back because too many went out to that recipient in a short time. Either way, retry after the `Retry-After` seconds. `QUOTA_EXCEEDED` is the other `429`: the operation would go over a quota of your plan for the current period (issued invoices, for instance). Retrying does not help until the quota resets or the plan changes. `error.details` carries `feature_code`, `current_usage`, `quota_limit` and `reset_date`, and `RateLimit-Reset` the seconds until the reset when it is known.
- `500`: Internal server error
- `default`: Any status code the operation does not list above. Every operation declares it, so a generated client always has a branch to fall into and never loses the cause of a failure it did not anticipate. This is where the transport-level answers land — `405`, `406`, `415` and `429` — together with any status a future version of the API starts returning. All of them carry the same error envelope as the codes listed explicitly, so `error.code` is what tells them apart: switching on the status code alone is not enough. See «Transport-level errors» in the API description for when each one is produced. A `402` carrying `SUBSCRIPTION_REQUIRED` lands here when the account has no active subscription. `error.details` says why and what to do: `current_status` (absent if it never had one), `required_permission` and `action`. A `503` carrying `SERVICE_UNAVAILABLE` lands here too, with a `Retry-After` header: a dependency of BeeL itself (the database, the email provider) was momentarily unavailable and nothing was done. Retry after those seconds. A `502` carrying `EXTERNAL_SERVICE_ERROR` also lands here: an outbound integration the operation depends on failed or did not answer in time. It is a transient condition — retry with the same `Idempotency-Key` where the operation accepts one. One exception to the envelope: a failure of the network edge that never reaches the application (`502`, `503`, `504`, `524`) is generated by Cloudflare and its body is not BeeL's — it may not even be JSON. Treat those as "no answer", and retry.

---

[API](https://skmtc.dev/beel/apis/beel-public-api.md) · [All operations](https://skmtc.dev/beel/apis/beel-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/beel/apis/beel-public-api/revisions/ffbc0ae31f79?raw)
