> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ripio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Infrastructure requirements

> What to set up on your pages, your servers and with Ripio for the widget to load, talk to its API and reach your webhooks: allowed origins, CSP, SRI, network and WebView settings.

The widget runs inside your page but talks to Ripio's servers directly, and Ripio's servers call yours. Each of those paths crosses a boundary your infrastructure controls — a browser policy, a firewall, a TLS check — and a closed one usually fails silently: a blank widget, missing logos, or a buy that never gets approved.

This page lists everything to open, per path. See [How it works](/crypto-as-a-service/widget/get-started/how-it-works) for the paths themselves.

### Checklist

| # | Where | What | If it's missing |
| - | - | - | - |
| 1 | Ripio onboarding | Register the origins of the pages that embed the widget | The widget can't reach its API and shows the error screen |
| 2 | Your page | Allow the widget's hosts in your Content Security Policy | The widget doesn't load, can't call its API, or renders without fonts or logos |
| 3 | Your page | Pin the bundle with `integrity` and `crossorigin` | The browser refuses to run it, or you run a file you didn't vet |
| 4 | Your servers | Let your backend reach the widget API over HTTPS | No session code, so no session |
| 5 | Your servers | Expose a webhook endpoint Ripio can reach | Buys requiring approval fail and results aren't delivered |
| 6 | Your app | Enable JavaScript in the WebView | The widget doesn't start |

### Hosts

| Environment | Widget (bundle, fonts, hosted page) | Widget API | Token logos |
| - | - | - | - |
| Sandbox | `https://b2b-crypto-widget-front.sandbox.ripio.com` | `https://b2b-crypto-widget-api.sandbox.ripio.com` | `https://statics.ripio.com` |
| Production | Sent by your Ripio contact | Sent by your Ripio contact | `https://statics.ripio.com` |

Every rule below is per environment: sandbox hosts don't serve production, and vice versa.

### Allowed origins

In an embedded integration, the widget's requests to its API leave from **your page's origin**, so the widget API only answers origins registered for it. Send your Ripio contact every origin — scheme, host and port — where you embed the widget, for each environment:

```text theme={null}
https://app.yourbank.com
https://staging.yourbank.com
```

* An origin is exact: `https://yourbank.com` and `https://www.yourbank.com` are two origins, and so are the same host on two ports.
* A page served from an unregistered origin fails at the very first request: the browser blocks the response, and the widget shows its error screen. The browser console reports it as a CORS error.
* A [WebView integration](/crypto-as-a-service/widget/get-started/webview) loads the widget's own hosted page, whose origin is already registered, so there's nothing to add for it.

### Content Security Policy

When you embed the widget, your page's CSP applies to it. Allow:

| Directive | Value | Why |
| - | - | - |
| `script-src` | The widget's host | The bundle |
| `connect-src` | The widget API's host | The widget's only backend |
| `font-src` | The widget's host | The [fonts](/crypto-as-a-service/widget/configuration/theming#fonts) the widget serves next to its bundle |
| `img-src` | `https://statics.ripio.com` `data:` `blob:` | Token logos from Ripio's CDN, plus QR codes and icons the widget generates |
| `style-src` | `'unsafe-inline'` | The widget injects its design tokens and your account theme as `<style>` elements |

For sandbox, merged into an existing policy:

```http theme={null}
Content-Security-Policy:
  script-src 'self' https://b2b-crypto-widget-front.sandbox.ripio.com;
  connect-src 'self' https://b2b-crypto-widget-api.sandbox.ripio.com;
  font-src 'self' https://b2b-crypto-widget-front.sandbox.ripio.com;
  img-src 'self' https://statics.ripio.com data: blob:;
  style-src 'self' 'unsafe-inline'
```

* **A blocked logo isn't an error.** If `statics.ripio.com` is missing from `img-src`, the widget falls back to an avatar with the token's initials. Everything still works, but it doesn't look the way it should.
* **Using nonces or `'strict-dynamic'`?** With `'strict-dynamic'`, browsers ignore host allowlists in `script-src`, so put your nonce on the widget's `<script>` tag as you do with your own.
* **`style-src 'unsafe-inline'` doesn't open your page to the account theme.** The theme isn't CSS: it's a list of token values that Ripio validates when it's stored and the widget validates again before applying it. See [Theming](/crypto-as-a-service/widget/configuration/theming).
* **WebView:** the hosted page ships its own CSP. There's nothing to configure.

### Subresource Integrity

There's no floating `bundle.js`: each release publishes a versioned file and its SHA-384 hash, which your Ripio contact sends you. Pin both:

```html theme={null}
<script
  type="module"
  src="https://b2b-crypto-widget-front.sandbox.ripio.com/bundle.v<version>.<hash>.js"
  integrity="sha384-<hash>"
  crossorigin="anonymous"
></script>
```

* **`crossorigin="anonymous"` is required.** Without it, the browser can't check `integrity` on a cross-origin script and refuses to run it.
* **Load the file straight from Ripio's host.** Don't re-host it, proxy it, or let a CDN optimizer on your side minify, bundle or defer it: any byte that changes breaks the hash, and a re-hosted copy loads its fonts from the wrong place.
* **Updating is a deliberate step.** A new release means a new file name and a new hash. Until you change your snippet, your users keep the version you pinned.
* The bundle is the only script the widget runs, so its hash covers all of the widget's code. Its fonts aren't covered: browsers don't support `integrity` on fonts.

### Your servers

**Calling the widget API.** Your backend calls [`POST /auth`](/crypto-as-a-service/widget/get-started/authentication) every time the widget opens:

* Allow outbound HTTPS from your backend to the widget API's host.
* Keep `client_id` and `client_secret` in a secrets manager, never in your app or page.
* The endpoint is rate-limited per account. If you request codes in bulk, expect a `429`.

**Receiving webhooks.** Ripio calls the URL configured on your account for the [webhooks](/crypto-as-a-service/widget/webhooks). The endpoint must:

| Requirement | Why |
| - | - |
| Use HTTPS with a certificate from a public CA | Ripio verifies the certificate. A self-signed or internal-CA certificate fails the connection. |
| Resolve only to public IP addresses | Ripio refuses to call a host if any of its DNS records points to a private address. |
| Answer at the final URL | Redirects (`3xx`) aren't followed. |
| Answer the approval request within 5 seconds | It's a single attempt, and a late answer means the buy doesn't execute. Account for your load balancer, WAF and cold starts. |
| Give your handler the raw request body | The signature covers the exact bytes Ripio sent. A framework or gateway that parses and re-serializes the body before your code sees it breaks verification. |
| Keep its clock in sync (NTP) | You reject messages more than 5 minutes old, so clock drift on your side rejects valid ones. |
| Hold your Ed25519 private key in a secrets manager | It signs every response. Whoever holds it can approve buys on your behalf. |

If your firewall only accepts inbound traffic from known addresses, ask your Ripio contact before going live.

### WebView

If you point a WebView at the widget's hosted page:

* **Enable JavaScript.** The widget is a web component and doesn't render without it. It doesn't use cookies or the browser's storage, so nothing else needs enabling.
* **Register the native bridge** if you want to renew sessions without reloading. See [Native Android and iOS](/crypto-as-a-service/widget/get-started/webview#native-android-and-ios).
* **Don't log WebView URLs.** The session code travels in the URL fragment, and your app sees it in navigation callbacks before the widget removes it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.