# Core concepts _Understand Evervault's model for encrypting and using sensitive data across Relay, Functions, Enclaves, and payments._ ![...]() Learn what Evervault does, how it fits into your architecture, and how to start building. This page connects the dots between our platform, your use case, and a secure integration path. ## Understand Evervault from the ground up Evervault is a developer-first platform for orchestrating secure data flows. You can collect, store, process, and forward sensitive information without increasing your compliance scope. It's designed to fit into your existing stack with minimal effort, so you can focus on building your own data orchestration. At its core, Evervault encrypts sensitive data into ciphertext which can be safely stored in your infrastructure and decrypted at runtime. ## You store encrypted data Evervault doesn't use a [traditional token vault model](https://evervault.com/blog/encryption-in-payments-evervault-encryption-vs-traditional-encryption). Instead, we use a dual custody encryption model that's faster and more secure. With this architecture, you store encrypted data and we manage the keys. There's no need for developers to handle encryption keys directly, and data is never stored in Evervault's infrastructure. Instead, sensitive information is collected through our products and instantly returned in an encrypted format, ready to store or pass to downstream systems. The dual custody model has two key advantages compared to traditional token vaults: - **It's faster**: There's no need for database lookups on our side, which means lower latency and better throughput. - **It's more secure**: Even if one side is compromised, the data is still safe. An attacker would need to breach both your infrastructure and Evervault to access decrypted values. It's tokenization, reimagined for performance, privacy, and stronger architectural boundaries. You can use [Card Collection](/cards/card-collection) to encrypt card holder data or use our [SDKs](/sdks) to encrypt any other data. ## Processing and forwarding data After your data is encrypted and stored in your infrastructure, the next step is securely processing and sharing it. This is what Evervault's core products are designed for: enabling encrypted data to move, transform, and integrate without compromising security or compliance. These tools open up a wide range of use cases, including: - Sending decrypted data to third-party services. - Encrypting incoming data from a third party before storage. - Fetching and decrypting sensitive data from your own backend, in transit. - [Relay](/relay): Relay is a configurable network proxy that encrypts or decrypts data in transit—between your app and your own APIs, or any third-party APIs. - [Functions](/functions): Functions are secure, serverless code environments that decrypt encrypted data at runtime, allowing you to process it without exposing it in your infrastructure. ## Evervault for payments Evervault gives you flexible tools for collecting card data and using it for self-orchestration, without compromising security or compliance. You can capture and manage your own credit card data, design smart routing logic, run multi-PSP setups, build anti-fraud workflows, etc. - Run operations on encrypted card data using our payments products, like performing network tokenization, running 3D Secure authentications, or sending and receiving data to and from PSPs—all without ever decrypting it yourself. - Enable point-to-point encrypted transport to third parties (like payment processors or fraud tools) using Evervault Relay, maintaining full PCI compliance while keeping data completely shielded from your app and infrastructure. - Stay out of scope for PCI: our Level 1 Service Provider status and architecture let you handle card data workflows without bringing your systems into PCI scope. Whether you're building a checkout flow, tokenizing cards, or routing data to partners, Evervault makes it secure and compliant, while maintaining high performance and a quality developer experience. ## Teams, apps, and sandboxes Evervault accounts are organized around teams, which act as the top-level container for everything you build. Within a team, you can create and manage multiple apps, each with its own configuration, keys, and data flows. Apps can be designated as either Production or Sandbox environments, depending on their purpose. [Sandboxes](/developers/sandbox) are purpose-built for testing, simulation, and pre-production validation. They provide isolated environments that mirror production behavior, but without risking real data. Every sandbox app includes access to all [Platform](https://evervault.com/pricing) features, including advanced tooling like simulated card updates, webhook event triggers, and custom policy testing. This lets you build and iterate confidently, ensuring that production data stays untouched while you fine-tune your integrations. > **Production data** > > Sandbox environments simulate all values and behaviors. If you require production-level data, such as card scheme responses, ACS server responses, or live webhook payloads, you'll need to contact support to access a production proof-of-concept (POC) environment for live testing. > > [Contact Support](mailto:support@evervault.com) ## Start building your integration You can integrate with Evervault using our client SDKs, server SDKs, REST API or CLI. With developer-friendly tooling and a fast integration path, engineering teams can go from idea to implementation in hours, not weeks. ## Before going live There's a [checklist](https://evervault.com/launch) on our website for going live but there are a few things worth noting here. Sandbox and Production apps don't share data, API objects, or encryption access. This means: - Data collected or created with a Sandbox app can't be used by a Production app - Any data or API objects you need for a Production app need to be created using that app's UUID and key - Data that gets encrypted in a Sandbox app can't be decrypted with a Production app All of that works in the opposite direction as well (e.g., data encrypted by a Production app can't be decrypted with a Sandbox app). If there are feature flags enabled for you during your integration process, [contact support](mailto:support@evervault.com) to ensure they're enabled for production as well. --- # Relay _Evervault Relay is a configurable proxy that encrypts or decrypts sensitive data in transit for your APIs and third‑party services._ Relay is a network proxy that encrypts and decrypts sensitive data in transit between a client and a destination server. It works with your own APIs and third-party services, and requires minimal changes to your existing code. ## How Relay works You can use Relay in a few ways: - Outbound from your server: you hold encrypted data (such as [card details](/cards/card-collection)) and forward it to a third-party API. Relay decrypts the data as the request passes through it, so your infrastructure never handles sensitive data directly. - Inbound to your server: data arrives from an external source and needs to be encrypted before it reaches your server. Relay encrypts the fields you specify on the way in. - Outbound from your server to your frontend (specifically for revealing card details in a browser): encrypted card details are decrypted on the way out so they can be displayed to customers through the [Reveal component](/cards/reveal). > To further process data encrypted by Relay, check out [Functions](/functions) and [Enclaves](/enclaves). Relay is most commonly used for outbound decryption. Here's what that looks like for collecting and sharing card data for a payment. 1. Your customer enters their card details into our [Card component](/cards/card-collection). The data is encrypted within the iframe before it's returned to your application. 2. Your backend receives and stores the encrypted card information. Because the data is encrypted, it's safe to store on your side. 3. Using the encrypted data, you send an API request through Relay to your payment processor. 4. Relay detects the encrypted card fields and decrypts them in transit. 5. The payment processor receives the card data in plaintext, completes the payment, and the response is piped back to you through Relay. Evervault doesn't do anything with the response from your payment processor. If needed, you can configure Relay to encrypt any sensitive values before the response is forwarded to your server. ## Getting started This example focuses on creating a relay and how you can send encrypted data through it. See the [Card Collection](/cards/card-collection#get-started-with-the-card-component) page for an end-to-end guide that covers collecting, encrypting, and forwarding card data. > Encrypt and decrypt actions are tied to app keys. If you encrypt data with Relay in a [sandbox app](/developers/sandbox), you can't decrypt that data with Relay from a production app (or the other way around). Data can only be encrypted and decrypted using the same app key. ### Create a relay Open the [Relays section](http://app.evervault.com/app/relays) in the dashboard and click **Create Relay**. Provide the base URL (`example.com`) for the API you want to proxy requests to and create the relay. Evervault provisions a domain for your relay after creating it. This is the domain you'll send requests to when you want to proxy API calls to downstream partners. You can find the domain in the dashboard (it looks this: `[DESTINATION_URL].relay.evervault.app`). ### Configure a route Routes define what to do with requests that match a given [URL path](#path-matching), and you can have multiple routes for each relay. You configure the [data type and fields](#selecting-fields) (JSON, form data, etc.) to operate on, as well as what to do with that data. To create a route, click **+ Add Route** in the dashboard and then set the path (e.g., `/charge`), actions, etc. You configure relays to encrypt or decrypt data with route actions. These actions can be applied to requests (data being sent to the destination) or the response (data being returned to you). The combination you configure depends on your use case. - Decrypt on request: your backend holds encrypted data and forwards it to a third-party API. Relay decrypts the fields in transit. - Decrypt on response: used with the [Reveal component](/cards/reveal) to display card details to your customers. - Encrypt on request: Encrypt data that you collect before storing it (outside of card data which you should use our [card collection](/cards/card-collection) for) - Encrypt on response: encrypt data returned by a third party, such as a card issuer returning PANs, before it reaches your server. ### Sending a request Pass your relay's domain into your requests to proxy them to the target endpoint. As an example, when you collect a card number with Evervault, it's encrypted on the frontend before being sent to your backend. So a card number like `4242 4242 4242 4242` is turned into something like this. ``` ev:QkTC:LUZWpxaiu16W1DJT:AnzlRtDd/kWFWuOsQH+HE0oqZHKCYZst+yZAQAFVBiXh:xsbavmwYTzgtKFWEcA8o9eMT+9olCFYKkTOtyFX158IrXn+G8MAtALr0pZrxBh0:$ ``` You store this value instead of the plaintext card number. When you want to charge the card, you [send the request through relay](/cards/card-collection?backend=node#integrate-the-relay) with the encrypted string. Relay decrypts it in transit, and your downstream partner receives the plaintext card number. ## Path matching Route actions will only be applied to requests that match the request path. Paths can be configured to match a specific path or multiple paths with a wildcard. | Example | Description | | --- | --- | | `/` | Matches requests to the root path | | `/checkout` | Matches requests to /checkout | | `/patients/*/reports` | Allows for dynamic URL segments such as IDs. Matches requests such as /patients/1/reports, /patients/2/reports, etc. | | `/**` | Matches all requests | ## Selecting fields Route actions are only applied to data that matches the list of selected fields defined on an action. Field selection can be configured on a per-content type basis. ### JSON Use [JSONPath](https://goessner.net/articles/JsonPath/) to select fields according to their path in the JSON object. JSON values must only contain encrypted strings (e.g., `{ "ssn": "ev:...1a2bc$" }`). If the value contains other data like raw text (e.g.,`{ "ssn": "User SSN: ev:...1a2bc$" }`), decryption fails. JSON payloads are supported for requests where the `content-type` subtype is JSON. The subtype is the part of the content-type header after the `/`. For example, a content-type of `application/json+charset=utf8` has a subtype of JSON. #### JSON selector examples ### Form data Form data is only supported for requests with a `content-type` of `application/x-www-form-urlencoded` or `multipart/form-data`. Form fields are matched directly by name. If a matched value contains multiple pieces of encrypted text, each one is decrypted individually. Global wildcards are supported for form data but not nested ones (e.g., `*` is valid but something like `name.*.last` isn't). > **Wildcard selectors also decrypt encrypted files** > > An `*` form-field decrypt selector also decrypts encrypted files that arrive as octet-streams, including file parts in `multipart/form-data`. This unlocks file-decryption flows and deviates from the field-level model used elsewhere. Only Evervault-encrypted files are decrypted, so plain, non-encrypted octet-streams pass through untouched. #### Form data selector examples ### Files Relay supports encrypting and decrypting files in `multipart` and `form-data` requests. Encrypted files are forwarded with an unchanged filename, but the bytes in the file are fully encrypted. There is a per-request limit of 25 MB for encryption and decryption. An `*` [form data](#form-data) decrypt selector also matches encrypted files, so it isn't always necessary to configure a separate file selector. #### File selector examples ### Headers Headers can be selected by name and aren't case sensitive. If the matched header value contains multiple pieces of encrypted text, each one is decrypted individually. #### Header selector examples ### XML To work with XML data, configure your relay for JSON and select the fields to encrypt or decrypt using standard JSONPath. To encrypt attributes, use advanced JSONPath selectors. #### XML selector examples ## Authentication API key authentication adds an additional layer of security to your relay. When enabled, requests through Relay require a valid Evervault API key in the `X-Evervault-Api-Key` header and your app's ID in the `X-Evervault-App-Id` header. > **Enable authentication for relays that decrypt data** > > Evervault strongly recommends enabling API key authentication for any relay configured to decrypt data. If your relay is pointed at your own API (which already has its own authentication), it may not be strictly necessary. When using Relay with a third-party API, API key authentication ensures that only your application can trigger decryption. You can enable authentication for your relay in the [Evervault Dashboard](https://app.evervault.com) by navigating to the **Authentication settings** page. > **Authorize access to decrypted data** > > Some flows deliver decrypted data directly to a client (e.g., the [Card Reveal](/cards/reveal) iframe fetching plaintext card data). In these cases, your backend endpoint is the only place where access control is enforced. Evervault has no context for which of your customers can access a given record, so authenticate the request and verify that the caller is authorized before the response is returned. ## Limitations The following limitations apply to all requests sent through Relay. If your application requires higher limits, contact [support@evervault.com](mailto:support@evervault.com) to discuss your requirements. | Description | Limit | | --- | --- | | Max payload size | 5mb | | Max file size | 25mb | | Max chunked size (transfer-encoding: chunked) | 700kb | | Connection Timeout | 120s | ## Alerts Alerts allow you to see errors in Slack or be sent to any webhook URL of your choice. You can create an alert for your relay inside of the Evervault Dashboard by navigating to **Relay → Alerts → Add Alert**. When you configure a custom webhook to receive alerts, the following fields will be in the response. **Payload** - `relayUuid` — The unique identifier for your relay. - `teamUuid` — The unique identifier for your team. - `errorMessage` — Message explaining what went wrong. - `evMessage` — Message about where the error has occurred. - `activityLink` — A link to view the failed transaction in the Evervault dashboard. - `timestamp` — The time the error occurred. - `errorType` — The relay [error code](#relay-error-codes). - `statusCode` — The HTTP status code of the response. - `statusGroup` — 4XX, 5XX, or ERR. - `inboundDomain` — Your relay domain. ## Error handling Errors originating from within Relay can be distinguished by the `X-Evervault-Error-Code` header. The responses will have a content type of `application/problem+json` and a payload of the following structure: ```json { "code": "error code used to pinpoint the specific issue in the reference below", "title": "human readable title for the error", "detail": "longer-form, human readable description of the error", "status": 0 // numeric http status code of the response } ``` **Error Codes** - `subdomain-not-found` — This error indicates you are sending a request to a relay (e.g., my-api-com.relay.evervault.app) which doesn't exist. Ensure you have entered the Relay subdomain correctly. Also, ensure you have configured your relay correctly by following our Relay documentation. - `request-timeout` — The target took too long to respond. The error can have many causes ranging from network issues to malformed client requests. Ensure the target domain URL is correct, wait a few moments and try again. If the error persists, reach out to the owner of the target domain through any support channels they have in place. - `service-unavailable` — The target refused the connection and could not be reached. Ensure the target host is healthy. We recommend that you implement retry behavior to mitigate the impact of brief issues with the network or target host. - `unsupported-content-type-in-response` — This error occurs when Relay is configured to perform response encryption and receives an unsupported content type as a response from the endpoint. Currently, Relay can only encrypt JSON, XML and multipart/form-data structured data. - `proxy-auth-failed` — There was an issue authenticating with the provided API key. Ensure that you are using the correct API key. - `content-length-required` — The Content-Length header is either not present or isn't a number. - `invalid-request` — This error indicates that an invalid hostname was provided while communicating with Relay in [forward proxy mode](#forward-proxy-mode). Ensure you have entered the downstream hostname correctly, and that it can be resolved when requesting it directly. - `forbidden` — The API key provided does not have sufficient grants to perform the actions requested. Please review the grants given to your API Key. - `payload-too-large` — This request exceeds the request size limit in Relay. The current per-request limit is 25 MB for encryption/decryption of multipart data, and 5 MB for all other content types. - `invalid-content-type` — The content type header received by Relay was malformed, preventing the body from being processed. - `internal-server-error` — There was an issue with Relay itself. Try again in a few moments. - `decrypt/forbidden` — A ciphertext's data policy prevented it from being decrypted. Ensure your data policy's configuration is correct. - `no-hostname-in-request` — This error indicates that Relay received a CONNECT request with no hostname while operating in [forward proxy mode](#forward-proxy-mode). Ensure you have entered the downstream hostname correctly, and that it can be resolved when requesting it directly. - `relay-not-found` — There was an issue finding a relay for the provided domain. Make sure you are using the correct domain. If your domain is correct but you are still seeing this issue, ensure you have Relay configured correctly. - `encrypt/pre-encrypted-data` — There was encrypted data where it wasn't expected. This can mean a number of things: - An inbound request header value contained Evervault encrypted data. - An inbound request payload had Evervault encrypted data as the value of one of the fields, which was not going to be encrypted. - The target URI for the inbound request had Evervault encrypted data as part of the path. - `proxy-error` — Relay experienced an unexpected networking error while communicating with the downstream service. Please confirm that the downstream service is accessible when not routing through Relay, and ensure that any allowlist requirements have been met. - `http-connect-not-allowed` — Relay received a request using the CONNECT method which is only supported in [forward proxy mode](#forward-proxy-mode). - `bad-request` — The server is unable (or refuses) to process the request sent by the client, due to an issue that is perceived by the server to be a client problem. - `proxy-authentication-required` — A relay with authentication enabled was requested without valid credentials. ## Custom domains Each relay is provisioned with its own unique `relay.evervault.app` domain. You can also configure additional custom domains for your relay in the Evervault Dashboard by navigating to **Relay → Settings → Domains**. To add a custom domain, add a CNAME record to your DNS provider that points to `custom-dns.relay.evervault.app`. You also need to add an ownership validation TXT record on the `_ev-custom-domain` subdomain of your custom domain. > DNS propagation can take anywhere from a few minutes to 24 hours depending on your DNS provider and the TTL settings on your domain. If your custom domain isn't resolving after adding the records, check that both the CNAME and TXT records have been added correctly and allow more time for propagation. ## Alternative configurations These configurations are possible with Evervault, but not generally recommended. They're only used in specific situations where the default options aren't adequate. ### mTLS mTLS (mutual TLS) lets Relay authenticate with the target server using a client certificate, adding an additional layer of transport security between Relay and the downstream service. This is typically only required by organizations with strict security policies that mandate mutual authentication at the network layer — most integrations don't need it. > mTLS must be enabled by Evervault. Contact [support@evervault.com](mailto:support@evervault.com) to request it for your relay. ### Forward proxy mode The [integration described above](#getting-started) is sufficient for most use cases, but there are some scenarios where you might need to use Relay in forward proxy mode (e.g., you can't change the hostname used by a third party library). Forward proxy mode requires installing the Relay certificate chain, and configuring the relevant traffic to route using Evervault Relay acting as [an HTTP CONNECT proxy](https://en.wikipedia.org/wiki/HTTP_tunnel). To integrate with Relay in forward-proxy mode, you need to: - Trust the Relay cert chain on your server. The cert can be obtained from [ca.evervault.com](https://ca.evervault.com). - Identify the best approach for establishing an HTTP tunnel in your preferred language. - For example, in Node.js this is likely using an Agent (which can be created using the [Evervault Node.js SDK](sdks/node#createrelayhttpsagent)), or in JVM-based languages this is through the VM's `http.proxyHost` configuration options. - Supply your proxy authentication credentials to Relay using basic auth. This can be done during either the initial `CONNECT` request or the subsequent proxied request using the `Proxy-Authorization` header. The value should be basic auth credentials, with your app ID as the username and your API key as the password. - Relay only proxies requests to hosts that have been explicitly created in your app and can't be treated as a generic forward proxy. You can test Relay as a forward proxy using curl: ```sh curl -x https://relay.evervault.com -U app_id:api_key https://example.com/foo -kv ``` ### Using fixed regions > Using fixed regions isn't generally recommended and Evervault doesn't guarantee reliability or provide an SLA when using a fixed region. Evervault operates an active-active infrastructure across multiple regions and availability zones. If you need to, you can specify specific regions (either `us` or `eu`) to use with Relay, but this doesn't come with an SLA from Evervault's side. To do this, add `us` or `eu` to your relay URL when making a call. For example, if your base URL is `your-prod-app-12345.relay.evervault.app`, change it to `your-prod-app-12345.us.relay.evervault.app` to send requests to the US region. - [Functions](/functions): Learn how to use Functions to process encrypted data with your own code. - [Card Collection](/cards/card-collection): Learn how to use Card Collection to collect cardholder data. --- # Functions _Evervault Functions are secure serverless runtimes that decrypt Evervault data at runtime so you can run custom logic safely._ Evervault Functions are secure serverless functions which allow you to process data encrypted by Evervault products. When you pass encrypted data to a function, it's automatically decrypted. You can then process this data by running custom logic written in Node.js or Python as you usually would, but without ever handling it in plaintext on your infrastructure. ## Deploying a function The quickest way to deploy your first function is to create a starter template by connecting your GitHub account. 1. Sign in to the Evervault Dashboard 2. Create or open an app and navigate to the **Functions** tab 3. Click **Create Function** 4. Select **Choose template** 5. Authenticate with GitHub 6. Select a starter template and deploy your function > **Using the CLI** > > You can also use the [Evervault CLI](/sdks/cli) to deploy a function directly > from your machine. ### Redeploying a function Evervault listens for webhooks from GitHub to determine when to redeploy functions. If function code is in the root directory, all changes in that repository redeploy the function. If function code is in a subdirectory, only changes within that subdirectory trigger a redeploy. ## Running a function Functions can be invoked using our [SDKs](/sdks), or using our [REST API](/api). Any encrypted data within the payload sent to the function is decrypted before being passed to the function handler. ### Client-side execution API keys are sensitive and shouldn't be used client-side. When invoking functions from frontend applications, we recommend using a run token. Run tokens are single use, time bound tokens for invoking a function with a given payload. Run tokens last five minutes and must be used with the same payload that was used to create the run token. Run tokens should be created in your backend using our [API](/api) and handed off to your client. ```javascript await fetch("https://api.evervault.com/functions/:function-name/runs", { method: "POST", headers: { Authorization: "RunToken ", "Content-Type": "application/json", }, body: JSON.stringify({ payload: { message: "ev:tk9d:wjfiw..." }, }), }); ``` ### Responses The response from a function run will contain a status field. The status field will be either `success` or `failure`, depending on whether the function completed successfully or not. Any payload returned from the function will be inside of the `result` field of the response object. ```json { "id": "func_run_bd9e16a08f18", "status": "success", "result": { "message": "Hello from a Function!" } } ``` #### Error handling If there is a failure in the course of a function run, the response will contain an error object with a `message` and `stack` field. The `message` field will contain a human readable error message, and the `stack` field will contain a stack trace of the error. ```json { "id": "func_run_6de494a86a7d", "status": "failure", "error": { "message": "Oh no! Something went wrong", "stack": "Error: Oh no! Something went wrong\n at exports.handler (/runtime/app/index.js:26:15)\n at /runtime/index.js:65:26\n at new Promise ()\n at /runtime/index.js:52:16" } } ``` > **FunctionNotReady** > > Functions that have been idle for extended periods of time may return > `FunctionNotReady HTTP 409` errors. This can be resolved by retrying the > request. ## Encryption within a function An `encrypt` function can be accessed through the context parameter. This allows you to encrypt data in your response using your app’s keys. ## Configuration Evervault Functions are configured using a `function.toml` configuration file which can be committed to source control. Simply provide a `function.toml` file in the root of your repository and Evervault automatically includes it at build time. ```toml [function] # Customize your function's timeout by providing a positive integer value. # The timeout is defined in seconds. # Timeouts of greater than 55 seconds are only permitted for asynchronous function runs. # Default: 30 # Maximum: 900 timeout = 45 # Customize your function's entry point by specifying the handler in the form `.` handler = "main.myFunc" # The name of your function name = "your-function-name" # The language of your function # Node: node@22, node@24 # Python: python@3.9, python@3.10, python@3.11 language = "node@24" ``` ### Dependencies When you deploy a function to Evervault, we locate your `package.json` (for Node.js) and `requirements.txt` file (for Python) and install any non-development dependencies. When using Node.js, if a `node_modules` folder is included then dependency installation is skipped; this can be used to include private dependencies. If there are any issues with your dependencies, for example if you have missed one in your `package.json`, an `InitializationError` is thrown. Python runtime images (`python@3.9`, `python@3.10`, `python@3.11`) include the `gpg` command line tool, so your function can shell out to it for operations that Python's PGP libraries don't support natively. ### Environment variables You configure environment variables for functions in the [Evervault Dashboard](https://app.evervault.com) or with the [CLI](/sdks/cli). To use environment variables to store secrets, there're some specific steps you need to take. #### Encrypted secrets > Mark all sensitive values (API keys, access tokens, credentials, etc.) as > secrets when adding them as environment variables. You can set an environment variable as a secret when you create it (either in the Dashboard or with the CLI). This encrypts the value at creation time using your app's keys. Secrets are stored and displayed in their encrypted form in every context except the function handler. At startup, the function runtime decrypts secrets and injects them as environment variables that you can access with `process.env` in Node.js or `os.environ` in Python. | Context | Value visibility | | --- | --- | | Evervault Dashboard | Encrypted | | CLI `Get` Command | Encrypted | | Function handler (runtime) | Plaintext | #### Storing partner API keys If your function calls a third-party, store its API key, credentials, or any other sensitive data as an environment variable and make sure to [set it as a secret](#encrypted-secrets). This ensures the key is never visible in plaintext in the Dashboard or logs, and is only decrypted inside the function. ### Networking #### Limiting access to your function By default, functions respond to requests invoked from any client with a valid Evervault API key or run token. You can limit access to your functions by adding IP addresses to the allowlist in the [Dashboard](https://app.evervault.com). This only allows your functions to be invoked when requests are made from IPs in that list. #### Limiting egress from your function By default, functions can send requests to any third-party endpoint. You can add domain names to the allowlist in the [Dashboard](https://app.evervault.com) to limit network access to the hostnames you specify. ### Limitations Evervault Functions have a maximum memory consumption of `1024MB`, 2 available CPU cores, and 512MB of ephemeral filesystem storage. If you require more memory or CPU cores, check out [Evervault Enclaves](/enclaves) — the easiest way to build, deploy, and scale Secure Enclaves. > Don't use ephemeral storage for sensitive data. It's possible for ephemeral > data to be available to future function invocations, which increases the > risk of an accidental data leak. There are two ways to pass data into functions, and each one has its own limit on the amount you can include. When you run a function with our SDKs or REST API, the payload used for invocation should be kept below 1MB. Separately, after the function is running, you can pull data into it (e.g., downloading and processing files, making an API call to retrieve some information). Data pulled into functions this way should be kept below 1GB. Rate limits are set at 100 RPM for [Sandbox apps](/developers/sandbox) and 6000 RPM in production. If you need higher rate limits, contact [support@evervault.com](mailto:support@evervault.com). ### Execution time Evervault Functions have a default execution time of 30 seconds. This can be increased to a maximum of 55 seconds (for synchronous invocations) by setting the `timeout` in the [function.toml](#configuration). #### Asynchronous functions Asynchronous function runs up to 15 minutes are also permitted. To execute a function in async mode, you must set the [async argument](/api#createFunctionRun-request-async) on your request payload to `true`. ```javascript fetch("https://api.evervault.com/functions/hello-function/runs", { method: "POST", headers: { "Content-Type": "application/json", Authorization: "Basic ", }, body: JSON.stringify({ async: true, payload: { name: "ev:Tk9D:oVPHsPvwFHNk73DU:AglNWOgZekolcrxdxSpZJOusBgE+C9eWSapGIZkgTsUj:JKeSkdhVE9SCXqQINID4oBRCE/VhTb56VWGqyObP:$", }, }), }); ``` ## Observability Logs for all function runs can be viewed inside of the [Evervault Dashboard](https://app.evervault.com). You can learn more about observability in our [observability guide](/developers/observability). - [Relay](/relay): Encrypt or decrypt data in transit—between your app and your own APIs, or any third-party APIs. - [Card Collection](/cards/card-collection): Learn how to use Card Collection to collect cardholder data. --- # Enclaves _Evervault Enclaves let you deploy Docker workloads in AWS Nitro Enclaves with built‑in TLS attestation, networking and tooling._ Evervault Enclaves are the easiest way to build, deploy, and scale applications in a [confidential computing](#what-is-confidential-computing) environment. They allow developers to easily deploy Docker containers in a secure enclave, powered by AWS Nitro Enclaves. They offer easy deployment, invocation, and attestation of secure enclaves without the engineering overhead. ## What is a secure enclave? A secure enclave — otherwise referred to as a trusted execution environment (TEE) — is a highly constrained compute environment which supports cryptographic attestation of the code that it's running. They have no persistent storage, no shell access, and no networking by default. They allow you to run sensitive workloads in completely segregated environments with heavily restricted external access. Evervault uses secure enclaves extensively throughout our stack, and they're used to secure parts of our infrastructure which handle sensitive data and key material. This includes the Evervault Encryption Engine (E3), which is powered by AWS Nitro Enclaves. ### Why use secure enclaves? Secure enclaves are essential for developers seeking strong security guarantees for sensitive workloads. They enable developers to ensure that their code remains untampered by malicious actors through attestation. Secure enclaves offer all the benefits of standard containerized compute environments, such as Amazon ECS or Kubernetes, while providing additional security through attestation and significantly restricted I/O (e.g., no persistent storage and no networking by default). Companies that often need to reassure clients about their security posture can delegate much of that responsibility to the secure enclaves architecture. This helps them secure deals with the most security-conscious customers without altering their software development processes. More importantly, secure enclaves provide developers with the most secure method for deploying sensitive workloads. They significantly reduce the risk exposure of data, applications, and storage from insiders and third parties. As a result, they're rapidly gaining traction among security-conscious businesses and attracting substantial investment from cloud and hardware vendors like AWS, Google Cloud, and Microsoft Azure. ### The anatomy of an enclave ![An illustration of the anatomy of an enclave.]() Evervault Enclaves are built on top of AWS Nitro Enclaves. A Nitro Enclave is an isolated set of cores on an AWS EC2 instance which provides a segregated environment for running sensitive workloads. Nitro Enclaves are only accessible from the parent EC2 instance over a local VSock channel, and they have no persistence or networking support. Evervault Enclaves build on this by making it easy to migrate existing Docker-based workloads by abstracting away VSock, supporting optional egress networking, and exposing attestation measures that can be verified by our open source clients. An Evervault Enclave is comprised of two processes to make this possible: the control plane, and the data plane. #### Control plane The control plane is a daemon process which runs on the host EC2 instance. The main function of the control plane is to act as a TCP passthrough for incoming TLS connections to the enclave. The control plane also proxies any outgoing network connections for the enclave, enforcing the defined allowlist, and produces the transaction logs for the enclave. #### Data plane The data plane runs within the enclave as a sidecar. The data plane performs several functions to allow your process to run without any major changes: - Proxies traffic from VSock to TCP: One of the biggest barriers to enclave adoption is the ability to use VSock with popular frameworks and languages. The data plane abstracts away the in-enclave networking and forwards incoming traffic to the loopback interface. Your process can bind to a TCP port as normal. - Terminating TLS: The data plane provisions a trusted TLS certificate to the enclave on start-up. This certificate is used to terminate TLS on incoming connections, ensuring that your requests are only decrypted within the enclave itself. - Authenticates requests: The data plane ensures that incoming requests have a valid Evervault API key with permissions to invoke the enclave. This feature can be disabled to support invoking the enclave from an untrusted client. - Decrypts Evervault encrypted data: Evervault encrypted data that's sent to an enclave is transparently decrypted within the data plane before forwarding to your process. This allows you to share encrypted data with your process and handle it in plaintext. - Enables easy attestation: The Evervault SDKs help you to ensure every request to your enclave is attested within the TLS handshake. This attestation protocol is enabled by the data plane's position in the enclave. When an Evervault client needs to attest an enclave, it pulls an attestation document from the data plane, and uses it to validate every subsequent connection. You can read more about our attestation protocol [here](/enclaves#attestation-in-tls). - Produces request transaction logs: Running scalable, production services within an enclave can be challenging due to the lack of observability. To combat this, the data plane produces a transaction log of every request. The transaction logs cover a minimal amount of information (status code, request path, standard HTTP headers etc.) to aid debugging without eroding the secure enclave's security model. This feature is configurable, and can be disabled. ## Getting started All accounts are enrolled in a trial which starts when you deploy your first enclave. The steps below get you up and running with a `Hello World` service, and a client capable of attesting it. ### Set up the Evervault CLI You can install the Evervault CLI by running the following command in your terminal: ```bash curl https://cli.evervault.com/v4/install -sL | sh ``` You will also need to have [Docker](https://docs.docker.com/get-docker/) installed on your system as the Enclaves CLI needs to perform Docker builds using the Docker CLI. **Authenticating the CLI** The Enclaves CLI currently only supports API key authentication. To set up a session in your terminal, you need to create a scoped app API key in the Evervault Dashboard. After you have the API key, you can authenticate the CLI by setting the following environment variables: ```bash export EV_API_KEY= export EV_APP_UUID= ``` ### Initialize your enclave To create your first enclave, you’ll need a server and a `Dockerfile`. If you don’t have one already, then you can use our [Hello, Enclave](https://github.com/evervault/hello-enclave) repository as a starting point. You can clone it by running the following command: ```bash git clone https://github.com/evervault/hello-enclave ``` After you have a server and a `Dockerfile`, you can initialize a new Evervault Enclave using the following command: ```bash ev enclave init -f ./Dockerfile \ --name hello-enclave \ --egress ``` This command generates a new `enclave.toml` [configuration file](#configuration) and registers a new enclave inside of your Evervault app. Note that we use the `--egress` flag to allow the enclave to make outgoing network requests. This is optional, and you can omit it if you want to create an enclave that only accepts incoming requests. > **Debug mode** > > During development, you may want to enable debug mode to access the logs from > the enclave. ### Build your enclave Now that we have our `enclave.toml`, we can build our enclave. The build command converts the service’s `Dockerfile` into an enclave image file (`.eif`). An `.eif` file is a binary image that we'll use to initialize an AWS Nitro Enclave. All attestation measures are based on the `.eif` binary. ```bash ev enclave build -v --output . ``` The first build can be slow (approximately 2 minutes) as the CLI has to build several Docker images. Subsequent builds should be faster because the images get cached. After the enclave is built, its PCRs are written to the `enclave.toml` file. This helps with tracking changes in attestation measures within source control. Our `enclave.toml` should now have a section that looks something like this at the end of it: ```toml [attestation] HashAlgorithm = "Sha384 { ... }" PCR0 = "8576aa759528d6dc82b6a35504edf491bcf245266acb5745f7f15801e15988a5abbc8c637af3edeb96efcbe8e8a433a1" PCR1 = "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f" PCR2 = "4ffe3d8b0211341c9eac73abccfcfed63f694a4a84b7758e70d1941d0ac6c0a7091c7860aa1ff2e4d39bbdd2b220608f" PCR8 = "9f357c7861268d124143701d30fbd0401f4f2854db7698851c51a08bc719abe9cc89645324d24cdbac1f216b482d6ad8" ``` These four PCR values are the attestation measurements that we will use to trust the remote server (the enclave) before we share any sensitive data with it. The four measurements reflect various aspects of the image you are running in the enclave. | PCR | Measures | Description | | --- | --- | --- | | PCR0 | Enclave Image File | A measure of the Image that will be run within the Enclave. | | PCR1 | Linux Kernel and Bootstrap | A measure of the kernel and boot ramfs data. | | PCR2 | User Application | A measure of the User Application without the boot ramfs. | | PCR8 | Signing Certificate | A measure of the certificate used to sign the Enclave Image File. | You can read more about attestation for enclaves [here](#attestation). The build command will also create two files: `enclave.eif` and `ev-user.Dockerfile`. The `enclave.eif` file is the image that the enclave runs in the AWS Nitro Enclave, and the `ev-user.Dockerfile` is the Dockerfile that was used to generate it. ### Deploy your enclave To deploy your enclave using an existing `.eif` you can run the following command: ```bash ev enclave deploy -v --eif-path $EIF_PATH ``` The CLI tracks your enclave deployment. After the deployment has stabilized, you should see a log which includes the domain for your enclave: ```bash Enclave deployed successfully. Your Enclave is now available at https://..enclave.evervault.com ``` > You can build a fresh `.eif` for a deployment by omitting the `--eif-path` > argument. This means you can skip the `ev enclave build` step and deploy in one > command. You can now call your enclave over the internet. If you deployed the Evervault `Hello, Enclave` template, you can use the following cURL command to try it out: ```bash curl https://..enclave.evervault.com/hello \ -H "API-Key: " ``` You should now see an echo response from your enclave: ```json { "response": "Hello! I'm writing to you from within an enclave" } ``` ### Attest your enclave With your enclave deployed and accessible, we can attest it to verify that its running the code we deployed. > **Debug mode** > > If your enclave is running in debug mode, the attestation measures will be all > zeroes. We strongly advise against running in debug mode in a production > setting. To attest the enclave, we'll take the PCR values that we emitted during our deployment and supply them to the Node SDK's `createEnclaveHttpsAgent` function. This function returns an HTTP Agent which can be used to validate that any connections to your enclave's hostname pass an attestation check before establishing a TLS connection. ```javascript const AttestationBindings = require("@evervault/attestation-bindings"); const encrypted = await evervault.encrypt("Your Data"); const enclaveHttpsAgent = await evervaultClient.createEnclaveHttpsAgent( { "my-enclave": [{ pcr8: "PCR8FULLHASH" }, { pcr0: "PCR0FULLHASH" }], }, AttestationBindings ); const response = await axios.post( "https://my-enclave.my-app.enclave.evervault.com", encrypted, { httpsAgent: enclaveHttpsAgent, } ); ``` You can read more about attestation for enclaves [here](#attestation). ## Encryption and decryption By design, data that is encrypted with Evervault that passes into the enclave is automatically decrypted. There is no further action required to decrypt and start using the data. If you need to manually encrypt or decrypt data within the enclave, you can use the [internal API](#internal-api). ## Internal API Evervault exposes a simple internal API within the enclave which allows you to encrypt or decrypt Evervault encrypted strings, as well as manually retrieve an attestation document if you wish to implement your own attestation protocol. This internal API runs on port `9999` by default, and is only accessible from within the enclave. ### /encrypt To encrypt data, you can make a request to `/encrypt` ```javascript app.post("/keys", async (req, res) => { const { publicKey, privateKey } = crypto.generateKeyPair(); const encryptedPrivateKey = await axios.post( "http://127.0.0.1:9999/encrypt", privateKey.toString("base64") ); return res.json({ publicKey, encryptedPrivateKey }); }); ``` ### /decrypt > By design, data that is encrypted with Evervault that passes into the enclave > is automatically decrypted. There is no further action required to > decrypt and start using the data. The purpose of the decryption API is to offer flexibility with applications built inside the Enclave. Examples could be if you need to upload a list of large files of encrypted data to be decrypted on start up of the enclave or a process that pulls in encrypted data from an external source. Another point to note is that when TLS termination is turned off in the data plane, it’s not possible for the request to be scanned for encrypted strings. This is another instance where the Decrypt API could be used. ```javascript const result = await axios.post("http://127.0.0.1:9999/decrypt", { encrypted: "ENCRYPTED_STRING", }); ``` > **Files aren't supported** > > These endpoints support strings, not files. To decrypt a file, use the > [Decrypt endpoint](/api#decrypt) or [Relay](/relay). ### /attestation-doc To fetch the attestation document for the enclave, you can make a request to `/attestation-doc`. You can provide a challenge, nonce, and public key to be included in the attestation document. Each field is optional. The challenge and nonce are taken as UTF-8 encoded values, and the `publicKey` is taken as a base64 encoded value. ```javascript const response = await axios.post("http://127.0.0.1:9999/attestation-doc", { challenge: "abc", nonce: "123", publicKey: "def", }); ``` ## Ports By default, traffic within the enclave is forwarded to port `8008` on loopback. This can be configured by supplying an `EXPOSE` directive in your Dockerfile. Certain ports are reserved within enclaves for internal services. In certain cases, these ports are only reserved when a feature is enabled. The reserved ports are as follows: | Port | Reason | Feature | | --- | --- | --- | | 9999 | Used by the Internal API | -- | | 53 | Used to proxy egress DNS calls | [Egress](#egress) | | 4444 | Used to proxy egress TCP traffic | [Egress](#egress) | ## Environment variables Environment variables can be configured using the UI or CLI. Secrets can be encrypted on creation and are made available to your enclave environment in plaintext on startup. When you create or modify an environment variable for your enclave, it only takes effect after you redeploy the enclave. You can [restart your enclave's current deployment](/sdks/cli#restart) to redeploy your existing enclave image without affecting it's PCRs. Details on how to update your enclave's environment variables are detailed in the [Enclaves CLI reference](/sdks/cli#enclaves) ## TLS termination TLS termination is enabled by default on your enclave. When enabled, any requests to your enclave terminate TLS within the enclave using a cert signed by a trusted CA. With TLS termination enabled, you have access to features like automatic field decryption, authentication using your Evervault API key, and transaction logging. You can disable TLS termination by setting `tls_termination=false` in your `enclave.toml` configuration file. With TLS termination disabled, you need to terminate TLS on all incoming connections within your own process. This allows for enclaves to perform client authentication for mTLS, or to use self-signed certificates. > If you disable TLS, you need to configure your Enclave to perform its > `healthcheck` on a different port to incoming traffic. This can be done using > the healthcheck section of the `enclave.toml` file. ## HTTP transaction logging When TLS termination is enabled on your enclave, you can enable HTTP transaction logging which propagates request logs to the Evervault **Activity Screen** within the dashboard. These logs contain HTTP status codes, methods, timestamps etc. This can be configured within the `enclave.toml` by setting `trx_logging = false` (default is true). A unique ID is also appended to each request. This ID can be found in the response headers under `x-evervault-ctx`. You can then search for this ID in the **Activity Screen**. ## Trusted headers By default, your Enclave's transaction logs obfuscate any non-standard HTTP headers. You can specify a list of headers to pass through using the `trusted_headers` key in the `enclave.toml` file. This can be used to surface error codes or similar diagnostic information from your enclave. For example: ```toml trusted_headers = ["X-Error-*", "X-Trace-Id"] ``` Certain headers cannot be trusted to avoid accidentally leaking credentials. This currently includes: `api-key`, `authorization`, and `proxy-authorization`. ## Debug mode If you want to test your Docker Image in an enclave and see the output, you can run your enclave in `debug` mode. Transaction logs are emitted and viewable through the Evervault Dashboard. Raw logs from your enclave can be viewed using the `ev enclave logs` command in the [CLI](/sdks/cli). > When running an enclave in Debug mode, the attestation measure returned consists entirely of zeroes. This means that an enclave in debug mode is not attestable. ## Egress By default there is no networking out of an enclave. If you wish to make requests out of the enclave, turn on egress by including `--egress` on enclave init or update the `enclave.toml`. You can also configure the allowed domains that requests out of the enclave should be allowed to with `--egress-destinations`. The list is enforced in both the data plane and the control plane. Requests to domains that are not explicitly allowed will fail during the DNS lookup. You can specify exact domains or wildcards to allow all subdomains, for example `enclave.evervault.com` or `*.evervault.com`. The default behavior is to allow requests to any domain but we recommend restricting them in production environments. ## Authentication Enclaves expect an Evervault API key in the `api-key` request header by default. This can be disabled if you wish to implement your own form of authentication to the enclave. This can be useful if your enclave is exposed directly to end users. ## Healthchecks By default, enclaves perform a shallow healthcheck. The communication between the host instance and the enclave is tested, and the enclave environment is validated to ensure that initialization completed successfully. This healthcheck can be extended to include a `GET` to the service running in the enclave. Every second, the `Evervault-Healthcheck-Agent` sends a `GET` request to your process. Your process is regarded as healthy if it responds with a successful status code (e.g., 2XX). You can configure your enclave to perform healthchecks on a dedicated port using the `healthcheck` section of the `enclave.toml` as shown in the toml reference below. This is particularly useful for enclaves that perform their own TLS termination. ## Concurrent request processing By default, the data plane accepts incoming connections serially. Each connection completes its TLS handshake before the next one is accepted, so a single slow client can delay every other connection waiting behind it. For workloads that handle many simultaneous connections, you can enable concurrent processing of connection acceptance and TLS handshakes. This improves networking performance by letting handshakes proceed in parallel instead of one at a time. Concurrent processing is opt-in and configured through the `[acceptor]` table in your `enclave.toml`. When the table is absent, the data plane keeps its serial behavior, so existing enclaves are unaffected. ```toml [acceptor] # The maximum number of connections to process concurrently. max_concurrent_connections = 1024 # The maximum number of TLS handshakes to perform concurrently. max_concurrent_handshakes = 256 # The timeout for a single TLS handshake, in milliseconds. handshake_timeout = 10000 ``` Each field is optional: - `max_concurrent_connections` caps the number of connections the data plane processes at once. Connections beyond this limit are terminated (meaning they aren't queued or processed at all). - `max_concurrent_handshakes` caps the number of in-progress TLS handshakes so the enclave isn't overloaded. - `handshake_timeout` limits how long a single TLS handshake can take, in milliseconds. Stalled handshakes are dropped after the timeout, freeing the slot for other clients. Two rules apply when setting these values: - Any value you set must be greater than zero. - `max_concurrent_handshakes` must not exceed `max_concurrent_connections`. A connection is accepted before its handshake begins, so extra handshake slots can never be used. An invalid `[acceptor]` table is rejected when you run `ev enclave init` and at build time, so misconfigurations fail fast rather than producing a broken enclave. > Tuning the connection cap is a tradeoff. A higher cap admits more concurrent > clients, but each connection holds a slot for its full lifetime. Set the cap > based on the resources available to your enclave and your expected connection > volume. ## Configuration Enclaves allow configuration to be embedded within the source code, so it is easily attestable and can't be tampered with. Simply include an `enclave.toml` file in the root of your repository and Evervault automatically includes it at build time. The `enclave.toml` can be used to configure rules such as network egress and debug mode. It also includes dynamic attestation measures so each time your enclave is built, the PCR measures can be verified by comparing the result of the attestation with the measures included in Evervault's TLS attestation. ```toml # The major version of Enclaves being used version = 1 # The name of your enclave name = "hello-enclave" # The ID of your enclave uuid = "" # The ID of the Evervault app which is hosting the enclave app_uuid = "" # The ID of the Evervault team which owns the app team_uuid = "" # Whether `debug` mode should be enabled. # Logs are only available in `debug` mode. debug = true # Whether API key auth is enabled for the enclave api_key_auth = true # Whether transaction logging is enabled for the enclave. # Note: Requires TLS termination to be enabled trx_logging = true # Define a list of headers to ignore during obfuscation of transaction logs. # Supports absolute matches and suffix wildcards. trusted_headers = ["X-Error-*", "X-Trace-Id"] # Turn off TLS termination in the data plane. # Note: you will have to handle TLS termination within your process. tls_termination = false # Enables forwarding proxy protocol when TLS termination is disabled. # Note: this prepends incoming requests with the binary proxy protocol. # If your in-enclave process does not expect proxy protocol, it will likely fail to parse requests. forward_proxy_protocol = false # The path to your enclave's `Dockerfile` dockerfile = "Dockerfile" # Whether or not to enable outbound network requests from your enclave # If enabled, the networking layer will be bundled at build time [egress] enabled = true destinations = ["*.evervault.com"] # Configuration for the in-enclave healthcheck. [healthcheck] # An endpoint exposed by your service to track its health within the enclave. # This endpoint will receive a `GET` request every second. # The healthcheck's user agent will be: "Evervault-Healthcheck-Agent" path = "/health" # The port that the healthcheck is being exposed on. # If unset, the healthcheck will be sent over the same port as incoming requests. port = 8081 # The amount of replicas you would like to run for your enclave. # Note: If only running one replica, there will be downtime # between deployments. (Default 2) [scaling] desired_replicas=2 # Opt-in configuration for concurrent connection acceptance and TLS handshakes. # If unset, the data plane accepts connections serially. [acceptor] # The maximum number of connections to process concurrently. max_concurrent_connections = 1024 # The maximum number of TLS handshakes to perform concurrently. max_concurrent_handshakes = 256 # The timeout for a single TLS handshake, in milliseconds. handshake_timeout = 10000 # The configuration for your in-enclave service. # If unset, the port will be taken as the last exposed port in the Dockerfile. [service] port = 8080 # The path to the signing key and certificate for your enclave [signing] certPath = "~/.ev/enclaves/cert.pem" keyPath = "~/.ev/enclaves/key.pem" ``` ## Best practices ### Writing the Dockerfile Since enclaves are built using Docker, a good place to start is making sure that you are [formatting your Dockerfile correctly](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/). First you need to use the `FROM` command which creates a layer from the Docker image you want to use. It’s best to use one of the container images available on [Docker Hub](https://hub.docker.com/) to ensure you are using a trusted source. You can also opt for using the `slim` version of the container which can help reduce the image size, however the `slim` versions may not contain all of the common packages in the default tag. ``` FROM node:24-alpine3.23 ``` Next you need to copy any files from your local source to the filesystem of the container. It’s best to use absolute paths in the Dockerfile. When your Docker container is converted to an enclave, your commands might not be run in the directory you expect them to be, so it is safer to reference your files as absolute paths. ``` COPY ./index.js /index.js COPY ./package.json /package.json COPY ./package-lock.json /package-lock.json ``` Make sure that you are exposing the non-reserved port you want to listen on since that is where traffic within the enclave is forwarded. The enclaves documentation uses PORT `8008` as an example, but this can be any available port that you define. ``` EXPOSE 8008 ``` Here are some examples of what your Dockerfile might look like. ### Dependency management Some of the instructions in your Dockerfile install required packages. You can use whichever package manager makes sense for your application. In some cases, you may want to use a manifest file that contains the required packages and their versions, like a `package.json` for Node or `requirements.txt` for Python. For Python specifically, it can also be helpful to use a [virtual environment](https://docs.python.org/3/library/venv.html). You can run the virtual environment within the container and then install the packages within the environment. This also helps with minimizing image size. Here is an example: ``` FROM python:3.13-slim RUN python3 -m venv /opt/venv COPY requirements.txt requirements.txt RUN /opt/venv/bin/pip install -r requirements.txt COPY app.py /app.py EXPOSE 8008 ENTRYPOINT ["/opt/venv/bin/python", "/app.py"] ``` ## What is confidential computing? Confidential computing allows teams to run their services in highly constrained compute environments, known as secure enclaves. While confidential computing can take many forms, they typically share two common features — restricted I/O and attestation. These two features are powerful building blocks in creating trustworthy, and secure services. ### Restricted I/O Secure enclaves have no persistent storage which dramatically reduces the attack surface for data exposure. They also prevent external access — meaning there’s no way for an attacker to gain direct access to a service at runtime. The only interface that the secure enclave exposes is the API that your service offers. ### Attestation Attestation is a way for the integrity of a service running in a secure enclave to be validated at runtime. This offers teams a guarantee that they're talking to a known version of their service. In practice, attestation is typically a two step process. Before a service is deployed to a secure enclave, it's measured and signed. Measuring the service involves computing checksums over the bundle that's deployed, and the signature allows teams to lock down deployments to trusted sources (e.g., environments that can sign the bundle using a known private key). When a client interacts with the service, they can request the attestation document and validate that it contains the expected checksums, and is signed by a known key pair. Allowing clients to verify the integrity of services that process sensitive data, can massively improve the security of a system by preventing sensitive data from being leaked to unknown or untrusted deployments. The Evervault Client SDKs for [Node](/sdks/node), [Python](/sdks/python), [Go](/sdks/go), [iOS](/sdks/ios), and [Android](/sdks/android) all expose attestation helpers which perform attestation on every connection to your enclave. This is done by pulling an attestation document from the enclave, and comparing its signed PCRs against the set of PCRs provided to the SDKs. If the PCRs are found to match, the challenge from the attestation document is compared against the certificate public key to confirm that the TLS connection is being terminated inside the enclave. You can then validate it locally using the `ev enclave attest` command in the CLI. This validates that the PCRs in the `enclave.toml` file match the PCRs returned in the handshake. The attestation doc for an enclave is publicly accessible through the `/.well-known/attestation` endpoint. This allows the Evervault SDKs to poll the attestation document and detect any changes resulting from deployments. You can request the attestation document using the following curl command: ```bash curl https://..enclave.evervault.com/.well-known/attestation-doc ``` #### Attestation in TLS Attesting a remote server can be difficult to get right, but can be extremely powerful. Evervault Enclaves embed the attestation check within the TLS handshake. This ensures that, when using an Evervault client, you cannot connect to an enclave that fails attestation. ##### CA provisioning ![A flowchart diagram of the CA provisioning flow]() When your enclave boots within Evervault's infrastructure for the first time, it generates a key pair to use for terminating TLS. The public key for this pair is then used to create an order with a trusted CA using the ACME process. The generated private key never leaves the enclave in plaintext. The Evervault managed process that runs on the host EC2 instance forwards this ACME order to the trusted CA. The trusted CA then returns an ACME challenge which is used to prove ownership over the requested domain. The enclave begins to poll the order status with the trusted CA, which eventually resolves to the issued cert. The ACME challenge, the generated public key, and the encrypted private key are uploaded to an AWS S3 bucket by the host process. This allows us to reuse the same key pair across deployments of your enclave, and speeds up subsequent deployments. The trusted CA then sends an HTTP challenge to the enclave's domain. This is routed to an Evervault managed service, which maps the challenge request to the challenge value stored in S3. We serve the challenge in the response to the trusted CA who then issues an end-entity certificate for our enclave. The order status being polled then resolves with a link to the location of our newly issued certificate. The enclave downloads the certificate and can use it to begin terminating TLS. The enclave also uploads this certificate to S3 via the host process to allow subsequent deployments, or other instances of the enclave to download and reuse the same certificate. ##### Performing attestation from the client ![A flowchart diagram of the attestion flow]() Now that the enclave has a trusted TLS certificate, we can walk through the process of how a client attests the enclave. First, when initializing a client, you map a set of enclave names to accepted PCRs. While the implementation differs based on the language being used, the client begins intermittently polling the enclave hostnames in the background. The polling requests hit the publicly available `/.well-known/attestation` endpoint which returns an attestation document. These attestation documents are then stored in an in-memory cache which is checked on every enclave call. So, when a client is sending a request to an enclave, it begins by performing a standard TLS handshake: - Validates the hostname corresponds with the requested domain - Validates that there is a valid trust chain going back to a trusted root CA. The client then attests the connection: - Checks the attestation document Cache for the requested enclave. - Validates the signature over the attestation document against the public AWS Nitro CA. - Validates that the embedded PCRs match a set of PCRs given to the client on initialization — the remote Enclave is running the expected service. - Confirms that the public key of the server cert is embedded within the attestation document. After all of the above checks have passed, the client can begin sending sensitive data into the enclave. This guarantees that the data is sent over a TLS connection directly to the enclave which is running the expected code. ## Enclaves trial All accounts are enrolled in a trial which starts when you deploy your first enclave. The terms of the trial are given below. ### How long is the trial program? Starting from the day you deploy your first enclave, you have full access to the Evervault platform (free plan) and enclaves for 14 days, so you can try building and deploying enclaves. After that period, you’ll have the option to sign up for monthly pricing to continue testing. If you’re unsure how long you have — you can always check the countdown timer in your Evervault Dashboard to see how much time is left in the allotment. If you need to adjust the timing or have any questions regarding the trial, feel free to reach out to our team. ### How many enclaves can I create? During the 14 day trial period, a team can run one Enclave. Enclaves are run as a single instance which may cause downtime for deployments, however this is only for your trial period. Following the initial trial, if you upgrade to a paid plan and start using enclaves in production, we will provide multiple instances with high availability and zero downtime deployments. ### What happens during the trial? You can play around with enclaves to see if it fits your use-case, free of charge. [Start here](#getting-started) if you’re not sure where to get started. We would love to know more about the use cases you are building for, any errors or bugs you encounter, and any feedback you have. You can contact us at [support@evervault.com](mailto:support@evervault.com). ### What happens after the trial? At the end of the 14-day period, we’ll notify you that your trial access has ended, and any active enclaves will be deactivated. Once deactivated, you will no longer be able to send requests to your enclave but it will remain visible in the Evervault Dashboard. If at any time you’d like to continue using enclaves, you can upgrade to a paid plan and we will restart your enclave instances. Upgrading to the paid plan will give you access to all of the [Evervault Pro](https://evervault.com/pricing) features and allow you to run multiple enclave instances with high availability and zero downtime deployments. ## Frequently asked questions ##### What is an AWS Nitro Enclave? [AWS Nitro Enclaves](https://aws.amazon.com/ec2/nitro/nitro-enclaves/) is a product from Amazon Web Services that allows you to create isolated Amazon EC2 instances with all of the security characteristics of a Secure Enclave. AWS Nitro Enclaves is available by default to customers of Amazon EC2, but building, deploying and scaling Nitro Enclaves can be a major time investment for engineering teams that have competing priorities. ##### How can I access the client IP address? There are two ways to see the original IP on the connection. When using TLS termination, the IP is included in the HTTP `X-Forwarded-For` header. If you have TLS termination disabled, you can receive the client IP via proxy protocol by setting the `FORWARD_PROXY_PROTOCOL` environment variable in your enclave. ##### How large can an enclave be? The current limit for enclaves is `16 vCPUs` and `64 gigabytes` of RAM. For trial usage, enclaves are capped at `4 vCPUs` and `16 gigabytes` of RAM. Enclaves use a RAM-based filesystem. This means that the memory allocated to your enclave is used for both storage and RAM. ##### Can I run an enclave on my own infrastructure? Evervault Enclaves are managed and run by Evervault on our infrastructure. A major advantage of using Enclaves is that the burden of hosting and scaling all of the infrastructure necessary to run secure enclaves is handled by Evervault. Evervault running your enclaves doesn't weaken the security guarantees provided by secure enclaves, thanks to attestation. You are still provided with the attestation measure at build-time and can verify that these haven't been tampered with — all within the TLS handshake. ##### Can I run multiple instances of an enclave? Yes, the number of instances an enclave runs on can be configured using the `desired_replicas` option in the `enclave.toml`. The EC2 instances running the enclave are split between two availability zones in a region for greater resiliency. During the trial period, enclaves run as a single instance. During this time, deployments result in downtime. ##### Can I restart my enclave? Enclaves can be restarted from either the **Versions** tab in the **Enclaves** section of the dashboard or by using the [CLI](/sdks/cli). ##### Why does my enclave perform decrypt requests on start-up? Enclaves order a trusted TLS certificate on their first deployment. The private key is encrypted in the enclave, and backed-up so it can be loaded in on every subsequent start-up. As a result, when each instance of the enclave launches, they perform a single decrypt request to load in their private key in plaintext. --- # Browser Tokens _Use sensitive data in browser automations without exposing it to your application code, your agent, or your browser platform._ > **Alpha** > > Browser Tokens are in alpha and subject to change. Contact > [support@evervault.com](mailto:support@evervault.com) for access. Browser Tokens let you use sensitive data in browser automation workflows without exposing that data to your application code, your agent, or your browser infrastructure. They're designed for agentic workflows and headless browser automations that need to enter payment cards, passwords, account credentials, API keys, or other sensitive values into third-party websites. You send sensitive values to Evervault and receive format-preserving token values along with proxy credentials. Only those tokens are passed to your agent or browser. When the browser submits a request through Evervault's browser proxy, Evervault replaces the token values with the original plaintext over the wire. ## Why use browser tokens Browser automations increasingly act on behalf of customers in real websites by logging into accounts, buying products, filling forms, and completing workflows. These flows often need sensitive data, which leaves three unappealing options. - **Give the sensitive data to the agent.** Your application, prompt context, model provider, and browser automation code all become part of the sensitive data handling path. - **Give the sensitive data to the browser platform.** Headless browser providers are strong at browser infrastructure, but they're rarely designed to handle PCI, credentials, or other regulated data. - **Use network or issuer-specific agentic payment tokens.** These carry issuer, country, and card brand availability constraints, and each one needs its own integration and user experience. Browser tokens are a compatibility layer instead. They work with any browser platform, and with any site where the sensitive value is ultimately submitted over the network. For payment flows, that value is usually a card number or CVV. For account access flows, it can be a password, recovery code, API key, or any other credential that shouldn't reach your agent, browser platform, logs, or automation code. ### Comparison to alternatives | Approach | How it works | Trade-offs | | --- | --- | --- | | Raw card data in the agent or browser | The agent or browser automation receives the card number and CVV directly, and enters them into the site. | Simple, but it expands PCI scope to the agent, browser scripts, browser platform, logs, and surrounding infrastructure. Most model providers and browser automation platforms aren't PCI compliant, so this isn't a practical option. | | Browser platform secret vault | The browser provider stores or injects sensitive values into the session. | Reduces prompt exposure, but puts the browser infrastructure provider in the sensitive data path. Values can still leak to the agent or automation scripts through the Chrome DevTools Protocol (CDP). | | Issuer or network agentic payment token | A card network or issuer creates a payment-specific token for supported transactions. | Strong where available, but coverage depends on issuer, card brand, geography, and merchant acceptance. Each card brand needs a separate integration, and behavior varies between them. | | Evervault Browser Tokens | Evervault returns format-preserving tokens, and reveals plaintext only through the browser proxy to allowed hostnames. | Works with existing browser platforms and merchant checkout flows, while keeping the agent and browser platform out of plaintext cardholder data handling. | ## How browser tokens work 1. **Collect and encrypt sensitive data.** Use [Card Collection](/cards/card-collection), a client-side or server-side SDK, or [Relay](/relay) to collect and encrypt sensitive values. These values can be considered non-sensitive and stored in your existing database. You can also provide plaintext values directly when that suits your integration. 2. **Create browser tokens.** Send the sensitive values to [`POST /browser-tokens`](#create-browser-tokens) with [policies](#permissions) that define where the values can be revealed, and optional [egress configuration](#egress-configuration). 3. **Pass token values to your browser automation.** Evervault returns [format-preserving tokens](#token-formats) that look like the original data, so card tokens are Luhn-valid and preserve the original BIN. 4. **Launch your browser through Evervault's proxy.** Route the session through the [browser proxy](#browser-proxy) using the returned credentials. This works with platforms such as Browserbase, Browserless, Kernel, and Steel, as well as local Chromium and Playwright flows. 5. **Evervault replaces tokens over the wire.** Your browser enters token values into the page. Evervault scans requests sent through the proxy and replaces tokens with the original plaintext, but only for allowed destinations. Evervault doesn't control, inspect, or interfere with the browser session itself beyond the proxy boundary. ## Security model Browser tokens are designed so that sensitive data never needs to be exposed to: - your application logic after token creation - your agent - your prompt context - your browser automation script - your browser platform This applies to credentials as well as card data. The browser automation can fill a password token into a login form. The agent, model provider, browser platform, and workflow logs only ever see the token value. Evervault never stores the plaintext or a server-side token mapping. Instead, the encrypted payload is embedded in a time-bound JSON Web Encryption (JWE) token, which is used as the authentication value for the browser proxy. The proxy uses the JWE to determine: - which token values exist for the session - what plaintext values they map to - which hostnames can receive decrypted values - when the token expires - what egress configuration to use If a request is sent to a hostname that isn't allowed by policy, the proxy behaves like a normal forward proxy and doesn't reveal the sensitive value. ## PCI scope reduction When implemented correctly, browser tokens can keep your browser automation platform, agent, and backend automation infrastructure out of PCI scope. Those systems never transmit, process, or store cardholder data. In this flow, cardholder data is collected and encrypted using Evervault-controlled collection surfaces before it reaches your automation stack. Your backend creates short-lived browser tokens from encrypted card data, receives format-preserving token values, and passes only those tokens to the agent or browser automation platform. The important boundary is that the systems driving the browser only ever handle tokens. - **Your agent** sees and reasons over token values, not the card number or CVV. - **Your browser automation code** fills token values into the page, not cardholder data. - **Your browser automation platform** renders, stores, logs, and transmits token values through the browser session, not plaintext cardholder data. - **Your backend infrastructure** stores encrypted values and creates scoped tokens, but doesn't need to store, log, or transmit plaintext cardholder data as part of the browser workflow. Plaintext cardholder data is revealed only inside Evervault's browser proxy, and only when a request is sent to a destination hostname allowed by policy. The merchant or PSP receives the original cardholder data over the network. The agent, browser platform, and automation backend stay outside the cardholder data environment because they never touch the plaintext value. To preserve this scope reduction, production integrations should: 1. Collect card data with [Card Collection](/cards/card-collection), an Evervault SDK, or [Relay](/relay). 2. Store only Evervault-encrypted card data in your systems. 3. Create short-lived browser tokens immediately before the automation session. 4. Pass only token values to the agent, browser runtime, logs, queues, and workflow systems. 5. Route the browser session through Evervault's browser proxy using the returned proxy credentials. 6. Scope reveal policies to the exact merchant or PSP hostnames the workflow needs. 7. Avoid logging, persisting, screenshotting, or otherwise exposing plaintext cardholder data anywhere in the automation stack. ## Create browser tokens Create browser tokens just in time for a specific browser automation session. ```bash curl -X POST 'https://api.evervault.com/browser-tokens' \ -H 'Content-Type: application/json' \ -u ':' \ -d '{ "data": [ { "category": "card.number", "value": "ev:encrypted:...", "id": "user-5577e072-primary-card-pan" }, { "category": "card.cvv", "value": "ev:encrypted:...", "id": "user-5577e072-primary-card-cvv" }, { "category": "credential.password", "value": "ev:encrypted:...". "id": "user-5577e072-merchant-login" } ], "ttl": 300, "permissions": [ { "action": "reveal", "target": "www.example-merchant.com" }, { "action": "decrypt", "target": "api.payment-gateway.com" } ], "proxySettings": { "type": "custom", "scheme": "https", "username": "egress-proxy-user-1ef0172b", "password": "egress-proxy-password", "hostname": "residential-ip-proxy.com", "port": 443 } }' ``` **Request body** - `data` `array` _(required)_ — The sensitive values to convert into browser-safe token values. Values can be Evervault-encrypted or plaintext. - `category` `string` _(required)_ — The semantic type of the value, such as `card.number`, `card.cvv`, `credential.password`. - `value` `string` _(required)_ — The sensitive value. Use Evervault-encrypted values for production integrations. - `id` `string` _(required)_ — The identifier for the data being tokenized. Used to correlate tokens within your system, and reflected in the Evervault logs. - `permissions` `array` _(required)_ — The rules that define where token values can be revealed as plaintext. See [Permissions](#permissions). - `action` `string` _(required)_ — The action to allow. Use `reveal`. - `target` `string` _(required)_ — The hostname that can receive the plaintext value. Use `*` to support open egress. We recommend restricting hostnames wherever possible to prevent data leakage. - `ttl` `number` — How long the tokens remain valid, in seconds. Defaults to `300`. - `proxySettings` `object` — The egress proxy configuration for the session. See [Egress configuration](#egress-configuration). - `type` `string` — Either `custom`, `datacenter` or `residential`. Use `custom` to bring your own egress proxy. - `scheme` `string` — Either `http` or `https`. Use `https` if your egress proxy supports CONNECT-over-TLS, otherwise use `http`. - `username` `string` — Required for `custom` proxySettings. Used as the username in basic auth with the CONNECT proxy. - `password` `string` — Required for `custom` proxySettings. Used as the password in basic auth with the CONNECT proxy. - `hostname` `string` — Required for `custom` proxySettings. The hostname of the CONNECT proxy for all egress calls. - `port` `number` — Required for `custom` proxySettings. The port of the CONNECT proxy for all egress calls. - `country` `string` — The ISO country code to use for geographic routing. - `region` `string` — The region or state to route through, where supported. - `city` `string` — The city to route through, where supported. - `postalCode` `string` — The postal or ZIP code to route through, where supported. The response contains a token for each value you sent, along with the proxy credentials for the session. ```json { "sessionId": "browser_3eecefc9ac41", "data": [ { "category": "card.number", "value": "4242424242424242", "id": "user-5577e072-primary-card-pan" }, { "category": "card.cvv", "value": "123", "id": "user-5577e072-primary-card-cvv" }, { "category": "password", "value": "uG7q9vK2pL", "id": "user-5577e072-merchant-login" } ], "proxyConfiguration": { "hostname": "us-va.browser.relay.evervault.app", "port": 443, "username": "app_...", "password": "eyJ..." }, "expiresAt": "2026-08-20T21:46:14Z" } ``` The `proxy.password` is a time-bound encrypted JWE. Evervault doesn't store the mapping between token values and plaintext values. ## Token formats Browser tokens are format-preserving where applicable, so they pass browser and merchant-side validation. | Data type | Token format | | --- | --- | | Card number | Luhn-valid, and preserves the original card length and BIN where applicable. | | Card CVV | Preserves digit length, including three-digit and four-digit CVVs. | | Expiry date | Usually doesn't need tokenization. The expiry month and year can typically be passed through as non-sensitive values. | | Email | Preserves email shape. | | Phone number | Preserves country or region shape and digit count where applicable. | | Password | Preserves a browser-compatible string shape. | ## Permissions Permissions define where token values can be revealed as plaintext. Scope each policy as tightly as possible. ```json { "action": "reveal", "target": "payments.example.com" } ``` - A token is revealed only when the browser request is routed through Evervault's browser proxy, and the destination hostname matches an allowed policy. - Multiple policies allow multiple destinations. Treat them as an allowlist. - If a merchant checkout posts card data to a PSP, gateway, or embedded payment iframe, include the hostnames that actually receive the cardholder data. - If the browser navigates or redirects to a hostname that isn't allowed, Evervault doesn't reveal the plaintext value to that hostname. - Keep policies environment-specific. For example, use separate hostnames for sandbox and production payment flows. Avoid broad policy rules. The intended production posture is to reveal only to the exact merchant, PSP, or gateway endpoints the workflow needs. ## Token lifecycle Token values are useful only with the matching proxy credentials and policy context. After expiry, the proxy credentials can no longer be used to reveal the underlying plaintext. 1. Store the underlying sensitive value as Evervault-encrypted data. 2. Create browser tokens immediately before launching or attaching to the browser session. 3. Pass token values and proxy credentials only to the systems that need them for that session. 4. Use the shortest `ttl` that gives the automation enough time to complete. 5. Discard token values and proxy credentials once the workflow finishes or the `ttl` expires. A workflow can take longer than expected because of login, multi-factor authentication (MFA), queueing, or human takeover. Set the `ttl` to cover that expected session duration rather than creating long-lived reusable tokens. ## Browser proxy Browser tokens use Evervault's browser proxy, which is exposed as an HTTP `CONNECT` proxy. ``` us-va.browser.relay.evervault.app # US Proxy eu-ie.browser.relay.evervault.app # EU Proxy ``` When launching your browser, configure the platform's proxy settings with the credentials returned by the Browser Tokens API. ### Trust the Evervault CA Because the browser proxy replaces token values with plaintext over the wire, the browser session must trust Evervault's certificate authority. Download the Evervault CA certificate from `https://ca.evervault.com`, then install it as a trusted root CA in the browser runtime before routing traffic through the proxy. The exact setup depends on the browser provider. - For local Chromium or Playwright, add the CA certificate to the operating system or browser trust store used by the automation runtime. - For hosted browser platforms, configure the session to trust the Evervault CA if the platform supports custom root certificates. - If the platform doesn't support custom root certificates, confirm whether it offers an equivalent certificate trust or proxy configuration path. > **Don't bypass certificate errors** > > Without the Evervault CA in the browser's trust store, HTTPS pages can show > certificate errors or block requests routed through the proxy. Configure CA > trust explicitly instead of disabling certificate verification. ## Launch a browser session The safest production pattern splits collection, token creation, and browser automation into separate steps. Your database stores encrypted card data, and your backend creates short-lived tokens. The agent and browser see only token values, so Evervault reveals plaintext to the allowed payment hostname alone. ### Hosted browser platforms Most browser platforms let you provide proxy configuration when creating or connecting to a session. The exact launch configuration depends on your platform. For example, using [Browserbase](https://browserbase.com) the integration would look like this: Connect your automation to the session, then use the token values returned by Evervault in your browser steps. ## Credential and account access flows Browser tokens aren't limited to payment data. The same pattern works anywhere a browser automation needs to submit a secret to a third-party website without exposing that secret to the agent or browser platform. Common credential use cases include: - logging into a customer account with a password - entering a recovery code or one-time backup code - submitting an API key into a developer portal - filling tax IDs, identity numbers, or other sensitive form fields - completing internal admin workflows that need privileged credentials The flow is the same as a payment flow. Collect or store the credential as an Evervault-encrypted value, then create a short-lived browser token scoped to the destination hostname. Give the agent and browser automation only the token value, and route the browser session through the browser proxy. Evervault reveals the plaintext credential over the wire to the allowed hostname. For example, if an automation needs to log into `accounts.example.com`, the agent can navigate the site and fill a password token into the password field. The browser platform sees a browser-compatible token string, and Evervault reveals the original password only when the login request is sent through the proxy to the allowed hostname. This keeps credentials out of prompts, traces, screenshots, session recordings, browser-provider logs, and application logs, provided those systems only receive token values. ## Egress configuration Browser tokens can be used with Evervault-managed egress IP configuration. Set the `proxySettings` object when creating tokens to control the type and location of the IP the session routes through. ```json { "proxySettings": { "type": "residential", "country": "us", "city": "ny", "postalCode": "10013" } } ``` Browser tokens only handle network egress. If your browser automation also needs timezone, locale, viewport, or geolocation alignment, configure those settings in your browser platform. ## Anti-bot compatibility Evervault's proxy is designed to preserve compatibility with real browser traffic. It uses TLS and HTTP client impersonation techniques, so network fingerprints such as JA4 match expected browser behavior. Evervault doesn't attempt to solve browser-level automation challenges such as CAPTCHAs, MFA, 3D Secure, or human-in-the-loop flows. Browser platforms often provide session takeover or human intervention features for those cases. ## Limitations Browser tokens are a network-layer compatibility layer. They don't replace browser automation, fraud controls, or checkout orchestration. - **Client-side encryption can require site-specific handling.** Some sites encrypt sensitive values in the browser before sending them over the network. Browser tokens still work where the site accepts raw values, but some PSP-specific flows need shims, which Evervault manages on your behalf. - **3D Secure and MFA remain application and browser concerns.** Frictionless 3D Secure works normally, but challenge flows usually require user interruption or browser-platform session takeover. - **Spending controls aren't enforced at the network layer.** Browser tokens can restrict where values are revealed, but transaction authorization stays with the issuer or payment processor. - **Tokens are time-bound rather than single-use.** A token can be used more than once within its `ttl`, so keep the `ttl` as short as the workflow allows. - **The browser session is outside Evervault's security boundary.** Evervault doesn't drive or inspect the browser session beyond proxying network traffic. Policies still ensure that sensitive data is never decrypted and revealed to a hostname you haven't explicitly allowed. ## Recommended integration pattern For production use: 1. Collect card or sensitive data with [Card Collection](/cards/card-collection), an Evervault SDK, or [Relay](/relay). 2. Store only Evervault-encrypted values in your database. 3. Create short-lived browser tokens when starting a browser automation session. 4. Scope policies to the exact hostnames the workflow needs. 5. Use the shortest `ttl` that gives your automation enough time to complete. 6. Configure the browser session with Evervault's proxy credentials. 7. Pass only token values to the browser automation or agent. 8. Log which token categories were revealed, and to which destinations, after the session completes. --- # Fingerprint _Use Evervault's Fingerprint API retrieve metadata for encrypted values._ > The Inspect API is now called [Fingerprint](/api#fingerprint). Existing integrations using the `/inspect` endpoint will continue to work, but migrate to `/fingerprint` when possible. Pricing is unchanged (Inspect pricing hasn't changed, and Fingerprint costs the same as Inspect). Use the [Fingerprint API](/api#fingerprint) to retrieve metadata about any encrypted value. For example, if you pass an encrypted card, you can retrieve the card brand, the last four digits, etc. ```javascript const response = await fetch("https://api.evervault.com/fingerprint", { method: "POST", headers: { "Content-Type": "application/json", Authorization: "Basic " + btoa(":"), }, body: JSON.stringify({ token: "ev:debug:Tk9D:HBpzdbFWXbX/N2cC:AyjMY/SKO49SlkXcDPtCGs+DxnUn/F8/lAtajCYZ/xT7:KV7AUn9vJJkZDtL8PKdOc8Y11yTL2vZQasFuHqM=:$", }), }); ``` The API responds with an object containing details about the encrypted data, as well as the card metadata inside of the `metadata` field. ```json { "type": "string", "category": "card-number", "encryptedAt": 1700067122000, "role": null, "fingerprint": "u0ljSwrqhhUv7GykyCHB/A2bgVGXq06fnG9bOI27Sbg/tAOz3myr5zsfbasEWuMLiH6jZayu3AHGfiB7l+Bbjg", "metadata": { "bin": "424242", "lastFour": "4242", "brand": "visa", "funding": "debit", "segment": "consumer", "country": "gb", "currency": "gbp", "issuer": "Gringotts Wizarding Bank and Trust Company" } } ``` ## BIN Lookup vs. Fingerprint | Metadata | Description | Fingerprint | BIN Lookup | | --- | --- | --- | --- | | Fingerprint | A unique identifier for the encrypted card. This can be used to detect card reuse. | ✓ | ✗ | | BIN | The first 6 or 8 digits of the card number. | ✓ | ✗ | | Last 4 | The last 4 digits of the card number. | ✓ | ✗ | | Brand | The card brand, such as Visa or Mastercard. | ✓ | ✓ | | Funding | The card funding type specifies the method by which transactions are financed (e.g. debit, credit). | ✓ | ✓ | | Segment | The card segment indicates the primary market or usage category of the card (e.g. consumer, etc.). | ✓ | ✓ | | Country | The country where the card was issued. | ✓ | ✓ | | Currency | The currency of the card. | ✓ | ✓ | | Issuer | The name of the card issuer. | ✓ | ✓ | | Product Name | The name of the card product. | ✗ | ✓ | | Fast Funds | Indicates whether the card supports fast funds (e.g. domestic, cross-border). | ✗ | ✓ | | Match Precision | Indicates how many leading digits of the submitted BIN are confirmed by the matched data. A higher value means a more specific match. When multiple matches are found, the value represents the least precise BIN range the submitted BIN could belong to. | ✗ | ✓ | | Three DS | Details on supported 3D Secure versions and ACS indicators for the card range. | ✗ | ✓ | See [BIN Lookup](/cards/insights-and-verification#bin-lookup) for retrieving metadata for a BIN range instead of a single encrypted card. - [Relay](/relay): Learn how to use Relay to encrypt or decrypt data in transit, without changing your application code. - [Functions](/functions): Learn how to use Functions to process encrypted data with your own code. --- # Card Collection _Collect and encrypt cardholder data in a PCI-compliant iframe, then safely use it with your APIs and processors via Relay._ You can collect card information in a few ways with Evervault, but our prebuilt UI components are the most common. Cardholder data that's encrypted with Evervault can be safely stored in your database and then shared with third-party payment processors using [Relay](/relay). This guide walks you through the entire process using our Card component for collection. ## The Card component The Card component is a secure iframe, hosted by Evervault, that you embed in your product to collect card data. When customers input their card information, it's encrypted within the iframe before being returned to you for storage. This approach reduces your [PCI DSS](https://www.pcisecuritystandards.org/standards/pci-dss/) scope because Evervault hosts the iframe, and you never handle plaintext card data. ## Get started with the Card component ### Install the Evervault SDK #### Browser Our [JavaScript SDK](/sdks/javascript) is distributed from our CDN, and can be installed by placing this script tag in the head of your HTML file. The SDK must be loaded directly from our CDN and cannot be bundled with your application or self hosted. ```html ``` Once the SDK is installed, initialize it using your Team ID and App ID. You can find these in the [Evervault Dashboard](https://app.evervault.com). ```javascript const evervault = new Evervault("", ""); ``` You can also install Evervault via the `@evervault/js` package on npm. This package is a light wrapper which handles loading the SDK from our CDN and also provides TypeScript definitions. ```javascript import { loadEvervault } from "@evervault/js"; const evervault = loadEvervault("", ""); ``` #### Frequently Asked Questions ##### Why does the SDK need to be loaded from the CDN? The SDK must be loaded directly from our CDN in order to be PCI Compliant. ##### How do I get my Team ID and App ID? You can find your Team ID and App ID in the [Evervault Dashboard](https://app.evervault.com). ##### Can I load the SDK asynchronously? You can load the SDK asynchronously using the `async` attribute on the script tag to prevent blocking the loading of your page. However, it is important to note that you will need to wait for the SDK to load before making any API calls. ```html ``` #### React Our React SDK is distributed via npm and can be installed using your preferred package manager. Once installed, Initialize the SDK by wrapping your application with the `EvervaultProvider` component. ```jsx import { EvervaultProvider } from "@evervault/react"; export default function App() { return ( ... ); } ``` #### Browser ### Create a Card component Initialize the Card component and mount it to a DOM element. See the [SDK documentation](/sdks/javascript#uicard) for available parameters. ```javascript const card = evervault.ui.card({ theme: evervault.ui.themes.clean(), }); card.mount("#card-details"); ``` #### React ### Render the Card component The `` component can be mounted in any child of the `EvervaultProvider`. The `onChange` prop is called every time the customer updates their card data, and includes the encrypted card data as well as validation information. You can use this to store the card data in state and pass it to your backend to process a payment. See the [SDK documentation](/sdks/react#card) for other available props. ```jsx import { Card } from "@evervault/react"; export function Checkout() { const [cardData, setCardData] = useState(null); const handleCheckout = () => { if (!cardData.isValid) { // The user has entered an invalid card. return; } // Pass the encrypted card data to your backend fetch("my-api.com/checkout", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ number: cardData.card.number, expiry: cardData.card.expiry, cvc: cardData.card.cvc, }), }); }; return ( <> ); } ``` You can customize the appearance of the Card component by passing a custom theme or extending one of our prebuilt themes. Read more about [customization](/cards/card-collection#customization). #### Browser ### Submit the encrypted card data to your API The encrypted card values can be accessed from the `card.values` object or by subscribing to the `change` event with the `card.on` method. Inside your submit handler, you can access the encrypted card data and pass it to your backend. ```javascript function handleSubmit() { // Pass the encrypted card data to your backend fetch("my-api.com/checkout", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ number: card.values.card.number, expiry: card.values.card.expiry, cvc: card.values.card.cvc, }), }); } ``` #### React Native Our React Native SDK provides a set of premade components for collecting and encrypting card data in a safe and secure way. ### Install the Evervault SDK Our React Native SDK is distributed via `npm` and can be installed using your preferred package manager. The [react-native-webview](https://www.npmjs.com/package/react-native-webview) package is a peer dependency and will need to be installed as well. Once installed, Initialize the SDK by wrapping your application with the `EvervaultProvider` component. ```jsx import { EvervaultProvider } from "@evervault/react-native"; export default function App() { return ( ... ); } ``` ### Render the Card component The `` component can be mounted in any child of the `EvervaultProvider`. The `onChange` prop is called every time your customer updates their card data, and includes the encrypted card data as well as validation information. You can use this to store the card data in state and pass it to your backend to process a payment. ```tsx import { Card, type CardPayload } from '@evervault/react-native'; import { Button, Form } from '@your/ui"; type CardFormProps = { onSubmit: () => void; }; function CardForm({ onSubmit }: CardFormProps) { const [data, setData] = useState(null); return (
); } ``` Learn more about the Card component in the [React Native SDK documentation](/sdks/react-native#card). ### Submit the encrypted card data to your API The last step is to submit the encrypted card details to your API so they can be forwarded on to your payment processor. ```tsx function Checkout() { const handleSubmit = (data: CardPayload) => { // Pass the encrypted card data to your backend fetch("my-api.com/checkout", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ number: data.card.number, expiry: data.card.expiry, cvc: data.card.cvc, }), }); }; return ( <> ); } ``` #### Swift The Evervault iOS SDK includes an `EvervaultInputs` module, which provides a SwiftUI view called `PaymentCardInput`. This view is designed for capturing credit card information and automatically encrypts the credit card number and CVC without exposing the unencrypted data. ### Install the Evervault SDK The Evervault iOS SDK can be installed using the Swift Package Manager. 1. Open your Xcode project. 2. Navigate to _File > Swift Packages > Add Package Dependency_. 3. Enter the repository URL for the Evervault iOS SDK: `https://github.com/evervault/evervault-ios.git` 4. Choose the latest available version or specify a version rule. After you install the SDK, you can initialize it with your Team ID and App ID. ```swift import EvervaultCore Evervault.shared.configure(teamId: "", appId: "") ``` ### Render the Card component To use `PaymentCardInput`, make sure you import the `EvervaultInputs` module in your file, and then add the view to your SwiftUI hierarchy. See the [SDK documentation](/sdks/ios#inputs) for available parameters. ```swift import EvervaultInputs struct ContentView: View { @State private var cardData = PaymentCardData() var body: some View { VStack { PaymentCardInput(cardData: $cardData) // Data captured: Text("Encrypted credit card number: \(cardData.card.number)") } } } ``` #### Android The Android SDK provides a Compose view called `PaymentCard`. This view is designed for capturing credit card information and automatically encrypts the credit card number and CVC without exposing the unencrypted data. ### Install the Evervault SDK Our Android SDK distributed via [maven](https://central.sonatype.com/search?q=com.evervault.sdk), and can be installed using your preferred build tool. #### Android Before using the Evervault Android SDK, you need to configure it with your Evervault Team ID and App ID. This step is essential for establishing a connection with the Evervault encryption service. You can find these in the [Evervault Dashboard](https://app.evervault.com). ```java Evervault.shared.configure("", "") ``` ### Render the PaymentCard view Once the SDK is installed and configured, you can use the `PaymentCard` view to capture encrypted card data. ```java UserParentLayout { val onDataChange: (PaymentCardData) -> Unit = {} // Handle card data // ... PaymentCard(onDataChange = onDataChange) { // User custom payment card layout using the [PaymentCardInputScope] components with a combination of user own components and modifiers } } ``` You can learn more about the `PaymentCard` view in the [Android SDK documentation](/sdks/android#inputs). ## Forwarding encrypted card data Now that you have the encrypted card data, you need to pass it to a third-party payment processor (Stripe, Adyen, etc.). To do this, you use [Relay](/relay) to decrypt the data after it leaves your infrastructure, and before it reaches the third-party API. Relay is a network proxy that can be configured to decrypt data during a request. When you proxy a request through Relay, the encrypted card holder data is decrypted, allowing the request to be processed as normal when it reaches the third-party. This means the encrypted card data you collect reaches the PSP as plaintext data, without you ever handling the raw form. ![An illustration of a credit card being decrypted by a relay.]() ### Create a Relay To simplify this example, we'll use [PutsReq](https://putsreq/) to simulate a third-party API. PutsReq provides a temporary endpoint that we can send requests to, however, in practice, this would be an endpoint from a third-party service. To create a relay for your PutsReq endpoint, navigate to the **Relays** tab in the [Evervault Dashboard](https://app.evervault.com), click **Create Relay**, and add the PutsReq endpoint to the destination field. ![Screenshot of the Relay creation form in the Evervault Dashboard.]() ### Configure the relay By default, relays are just transparent proxies that don't decrypt any data. They can be configured to perform encrypt and decrypt operations on request or response. For this example, the relay needs to decrypt encrypted card data on request as it's proxied to the third-party endpoint. In the Dashboard, click the **Add Route** button to configure a new route. We want to decrypt any data being sent to this endpoint, so we can enter `/**` in the path field to match all requests sent to the relay. Next, we can add a request action to decrypt any encrypted data in the request body. Select **Add Request Action** -> **Decrypt** -> **JSON**, and enter `$..*` in the fields to decrypt. This configures the relay to match any encrypted JSON fields in the request body and decrypts them. You can learn more about field selection in the [Relay documentation](/relay#selecting-fields). ![Screenshot of a Relay configured to decrypt all data in the request body.]() ### Integrate the relay Finally, we can implement our API endpoint to pass encrypted card data to the third-party payment processor using the relay you just created. When requests are sent, card data is automaitcally detected and decrypted before it reaches the third-party API. #### Node ```javascript // Replace with Relay URL const relayURL = ""; // POST /checkout router.post("/checkout", async (req, res) => { try { const response = await fetch(relayURL, { method: "POST", headers: { "Content-Type": "application/json", "X-Evervault-App-Id": "", "X-Evervault-Api-Key": "", }, body: JSON.stringify({ number: req.body.number, expiry: req.body.expiry, cvc: req.body.cvc, }), }); const data = await response.json(); res.json(data); } catch (error) { res.status(500).json({ error: error.message }); } }); ``` #### Python ```python # Replace with Relay URL relay_url = "" # Route handler for /checkout @app.route("/checkout", methods=["POST"]) def checkout(): try: payload = { "number": request.json["number"], "expiry": request.json["expiry"], "cvc": request.json["cvc"] } headers = { "Content-Type": "application/json", "X-Evervault-App-Id": "", "X-Evervault-Api-Key": "" } response = requests.post(relay_url, json=payload, headers=headers) return jsonify(response.json()) except Exception as e: return jsonify({"error": str(e)}), 500 ``` #### Ruby ```ruby # Replace with Relay URL relay_url = "" # POST /checkout post "/checkout" do begin payload = { number: params[:number], expiry: params[:expiry], cvc: params[:cvc] } headers = { "Content-Type" => "application/json", "X-Evervault-App-Id" => "", "X-Evervault-Api-Key" => "" } response = HTTParty.post( relay_url, body: payload.to_json, headers: headers ) content_type :json response.body rescue => e status 500 { error: e.message }.to_json end end ``` #### Java ```java // Replace with Relay URL private static final String RELAY_URL = ""; @PostMapping("/checkout") public ResponseEntity checkout(@RequestBody Map requestBody) { try { // Create request payload Map payload = new HashMap<>(); payload.put("number", requestBody.get("number")); payload.put("expiry", requestBody.get("expiry")); payload.put("cvc", requestBody.get("cvc")); // Set up HTTP client HttpClient client = HttpClient.newHttpClient(); // Create request with headers HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(RELAY_URL)) .header("Content-Type", "application/json") .header("X-Evervault-App-Id", "") .header("X-Evervault-Api-Key", "") .POST(HttpRequest.BodyPublishers.ofString(new ObjectMapper().writeValueAsString(payload))) .build(); // Send request and get response HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); return ResponseEntity.ok(response.body()); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(Map.of("error", e.getMessage())); } } ``` #### PHP ```php // Replace with Relay URL $relayUrl = ""; // POST /checkout app->post('/checkout', function ($request, $response) use ($relayUrl) { try { $data = $request->getParsedBody(); $payload = json_encode([ 'number' => $data['number'], 'expiry' => $data['expiry'], 'cvc' => $data['cvc'] ]); $ch = curl_init($relayUrl); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'X-Evervault-App-Id: ', 'X-Evervault-Api-Key: ' ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); $response->getBody()->write($result); return $response->withHeader('Content-Type', 'application/json'); } catch (Exception $e) { $response->getBody()->write(json_encode(['error' => $e->getMessage()])); return $response->withStatus(500)->withHeader('Content-Type', 'application/json'); } }); ``` #### Go ```go // Replace with Relay URL var relayURL = "" // POST /checkout func checkoutHandler(w http.ResponseWriter, r *http.Request) { var requestBody map[string]string if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } payload := map[string]string{ "number": requestBody["number"], "expiry": requestBody["expiry"], "cvc": requestBody["cvc"], } payloadBytes, err := json.Marshal(payload) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } req, err := http.NewRequest("POST", relayURL, bytes.NewBuffer(payloadBytes)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Evervault-App-Id", "") req.Header.Set("X-Evervault-Api-Key", "") client := &http.Client{} resp, err := client.Do(req) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer resp.Body.Close() w.Header().Set("Content-Type", "application/json") w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) } ``` > **Relay authentication** > > Notice we are sending the `X-Evervault-App-Id` and `X-Evervault-Api-Key` > headers to the Relay. These headers are used to authenticate the request to > the Relay. We recommend enabling [Relay > authentication](/relay#authentication) when sending requests to third-party > APIs. ## Customization The Card component can be fully customized to match the design of your application. By default, the Card component has no styling applied. You can use one of our prebuilt themes to get up and running or build your own theme from scratch. ### Prebuilt themes The SDK has three themes to get you started: `clean`, `minimal`, and `material`. #### Browser ```javascript evervault.ui.card({ theme: evervault.ui.themes.clean(), }); ``` #### React ```jsx import { Card, themes } from "@evervault/react"; function Checkout() { return ; } ``` ### Custom themes A theme is just an object with a `styles` property. The `styles` property uses a CSS-as-JS format to define CSS rules for the component. These rules are compiled to CSS and injected into the iframe. You can pass a custom theme as an argument to any of the prebuilt themes to extend them. ```javascript evervault.ui.themes.clean({ styles: { label: { color: "#6633ee", }, }, }); ``` #### Element attributes Although you can customize the CSS inside of the iframe, you can't modify the HTML. We know that layout can have a big impact on style definitions and so to help with this, we apply custom attributes to various elements within the iframe. These attributes are prefixed with `ev-`. You can see our premade [UI component themes](https://github.com/evervault/evervault-js/tree/master/packages/themes) for an example of how these attributes can be used when creating themes. **fieldset attributes** The following attributes are added to the `
` tag that wraps the entire component. - `ev-component` `Card` — The name of the component. - `ev-valid` `true | false` — Whether or not all of the fields within the component are valid. **.field Attributes** Each field in the component is wrapped in a
with a '.field' class and the following attributes. - `ev-name` `name | number | expiry | cvc` — The name for the individual field within the component. - `ev-valid` `true | false` — Whether or not the field is valid. - `ev-has-value` `true | false` — Whether or not the input has a value. ### Custom fonts You can load additional fonts from Google Fonts by providing a `fonts` array in the theme definition. Currently, we only support custom fonts via Google Fonts. ```javascript const theme = { fonts: ["https://fonts.googleapis.com/css2?family=Comic+Neue"], styles: { input: { fontFamily: "'Comic Neue'", }, }, }; ``` ### Responsive styling You can define media queries inside of the themes `styles` object, however, this may lead to unexpected behaviour as the media queries are matched against the iframe document, not the parent document. To get around this, we provide a `media` utility which allows you to define styles based on media queries that match the parent document. To access the media utility, you need to define your theme as a function that returns a theme object. This function is passed a utilities object as an argument, which contains the `media` utility. The media utility should be spread into the styles object of the returned theme. ```javascript const theme = (utilities) => { return { styles: { label: { fontSize: 16, }, ...utilities.media("(max-width: 400px)", { label: { fontSize: 20, }, }), }, }; }; ``` ### Custom card brands By default, the card component recognizes a fixed set of card networks but you can extend it with your own brands using `evervault.brands.create`. This creates a brand which you can then pass into the card component with the `customBrands` option. #### Browser ```javascript const acmeBrand = evervault.brands.create("acme-card", { numberValidationRules: { ranges: [[88000, 88999]], lengths: [16], }, securityCodeValidationRules: { lengths: [3], }, iconSrc: "https://your-cdn.com/acme-icon.svg", }); const card = evervault.ui.card({ customBrands: [acmeBrand], }); card.on("change", (data) => { // "acme-card" appears in data.card.localBrands when a matching number is entered console.log(data.card.localBrands); }); ``` #### React ```jsx import { useMemo } from "react"; import { Card, useEvervault, BrandOptions } from "@evervault/react"; const acmeBrand: BrandOptions = { numberValidationRules: { luhnCheck: true, ranges: [9900, [88000, 88999]], lengths: [16], }, securityCodeValidationRules: { lengths: [4], }, iconSrc: "https://example.com/logo.png", }; export function Checkout() { const evervault = useEvervault(); const customBrands = useMemo(() => { if (!evervault) return; return [evervault.brands.create("acme-card", acmeBrand)]; }, [evervault]); return ( { // "acme-card" appears in data.card.localBrands when a matching number is entered console.log(data.card.localBrands); }} /> ); } ``` The `ranges` argument accepts a BIN prefix (e.g., `9900`) or an inclusive range of prefixes (e.g., `[88000, 88999]`). The card component always accepts custom brands, even if `acceptedBrands` is set to restrict standard networks. #### Swift ## Customization Internally, the `PaymentCardInput` view uses SwiftUI `TextField`. These can be customized using SwiftUI modifiers like any other `TextField` in your application: ```swift PaymentCardInput(cardData: $cardData) .font(.footnote) .foregroundColor(.blue) ``` ### Built-in styles To provide more customization options, the `PaymentCardInput` can be styled using a `PaymentCardInputStyle`. There are two built-in styles: #### Inline ![Inline card input]() The inline style renders the credit card number, expiry and cvc fields all on a single row. ```swift PaymentCardInput(cardData: $cardData) .paymentCardInputStyle(.inline) ``` #### Rows ![Rows card input]() The rows style renders the credit card number on a single line with the expiry and cvc fields next to each other below. ```swift PaymentCardInput(cardData: $cardData) .paymentCardInputStyle(.rows) ``` ### Custom styles If these two styles do not match your use case, you can create your own style: ```swift struct CustomPaymentCardInputStyle: PaymentCardInputStyle { func makeBody(configuration: Configuration) -> some View { VStack(alignment: .center) { configuration.cardImage Text("CC Number").font(.title3) configuration.cardNumberField Divider() Text("Expiry").font(.title3) configuration.expiryField Divider() Text("CVC").font(.title3) configuration.cvcField } } } ``` #### Android ## Customization The `PaymentCard` and its components can be customized to fit your application's design. The view accepts a number of parameters that enable you to customize the modifier, text, and placeholder styles. The content parameter accepts a `@Composable PaymentCardInputScope.()` function, which can be used to customize the view's layout. The `PaymentCardInputScope` components can be customized with a modifier (Modifier), label (String or `@Composable`), placeholder (String or `@Composable`), text style (TextStyle), and input field colors (TextFieldColors). ### Prebuilt themes The SDK has two prebuilt styles to get you started. #### Inline ![Inline card input]() The inline style renders the credit card number, expiry, and cvc fields on a single row. ```kotlin @Composable fun InlinePaymentCard( modifier: Modifier = Modifier, textStyle: TextStyle = TextStyle.Default, placeholderTexts: PlaceholderTexts = PlaceholderDefaults.texts(), placeholderTextStyle: TextStyle = textStyle.copy(color = MaterialTheme.colorScheme.secondary), onDataChange: (PaymentCardData) -> Unit = {} ) ``` #### Rows ![Rows card input]() The rows style renders the credit card number on a single line with the expiry and cvc fields next to each other below. ```kotlin @Composable fun RowsPaymentCard( modifier: Modifier = Modifier, textStyle: TextStyle = TextStyle.Default, placeholderTexts: PlaceholderTexts = PlaceholderDefaults.texts(), placeholderTextStyle: TextStyle = textStyle.copy(color = MaterialTheme.colorScheme.secondary), onDataChange: (PaymentCardData) -> Unit = {} ) ``` ### Custom styles If these two layouts do not fit your application's design, you can create your own layout by passing a `@Composable` function to the content parameter. The `@Composable` function receives a `PaymentCardInputScope` (together with the `PaymentCard` Modifier) object, which contains the `CardImage`, `CardNumberField`, `ExpiryField` and `CVCField` fields. You can use these fields to create your own layout. ```kotlin UserParentLayout { val onDataChange: (PaymentCardData) -> Unit = {} // Handle card data // ... PaymentCard(onDataChange = onDataChange) { modifier -> Column( modifier = modifier .border(BorderStroke(1.dp, Color.LightGray), RoundedCornerShape(8.dp)) .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp), ) { CardImage() CardNumberField( modifier = Modifier.fillMaxWidth(), label = { Text( text = LabelTextsDefaults.CreditCardText, color = Color.Blue ) }, placeholder = { Text( text = PlaceholderTextsDefaults.CreditCardText, color = Color(0x75757575), fontSize = 12.sp ) }, textStyle = MaterialTheme.typography.titleLarge, textFieldColors = customTextFieldColors() ) ExpiryField( modifier = Modifier.fillMaxWidth(), label = { Text( text = LabelTextsDefaults.ExpirationDateText, color = Color.Blue ) }, placeholder = { Text( text = PlaceholderTextsDefaults.ExpirationDateText, color = Color(0x9E9E9E9E), fontSize = 10.sp ) }, textStyle = MaterialTheme.typography.titleMedium, textFieldColors = customTextFieldColors() ) CVCField( modifier = Modifier.fillMaxWidth(), label = { Text( text = LabelTextsDefaults.CvcText, color = Color.Blue ) }, placeholder = { Text( text = PlaceholderTextsDefaults.CvcText, color = Color(0xBDBDBDBD), fontSize = 8.sp ) }, textStyle = MaterialTheme.typography.bodyMedium, textFieldColors = customTextFieldColors() ) } } } @Composable private fun customTextFieldColors(): TextFieldColors = TextFieldDefaults.colors( focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent, disabledIndicatorColor = Color.Transparent, focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent, disabledContainerColor = Color.Transparent, ) ``` - [3D Secure](/cards/3d-secure): Learn how to use 3D Secure to authenticate your customers and reduce fraud. - [Network Tokens](/cards/network-tokens): Learn how to use Network Tokens to protect your customers' cards and improve authorization rates. ## Other collection methods For card collection, we generally recommend using our UI components, but if those don't work for your use case, you can also collect card information server to server and with our SDKs (each SDK has an `encrypt` method). These options can impact your compliance scope depending on what you implement. If the flow you build exposes you to plaintext card data, it can increase your PCI DSS scope. For server to server, create a relay that encrypts data for inbound requests to your server. The external server making the call needs to send the request to your relay, which encrypts the sensitive data and then forwards the request to your server. For client side encryption with the SDK, use the `encrypt` method. --- # 3D Secure _3D Secure is an online payment security protocol designed to reduce fraud and provide an additional layer of authentication for online transactions._ ![An illustration of a 3D Secure authentication process]() 3D Secure is an online payment security protocol designed to reduce fraud and provide an additional layer of authentication for online transactions. When a customer makes an online purchase, 3D Secure requires them to complete an additional verification step with the card issuer, typically through a password, biometric authentication, or a one-time passcode sent over SMS. ## The benefits of 3D Secure Implementing 3D Secure for online payments provides several benefits for both merchants and customers, enhancing overall transaction security and achieving regulatory compliance. - **Reduced fraud**: By requiring an additional authentication step, 3D Secure significantly decreases the risk of unauthorized transactions. This added security measure helps to ensure that the person using the card online is the legitimate cardholder. - **Liability shift**: One of the key advantages for merchants using 3D Secure is the liability shift. If a transaction is authenticated using 3D Secure and later turns out to be fraudulent, the liability for the chargeback shifts from the merchant to the card issuer. This can result in substantial cost savings and reduced chargeback rates for merchants. - **Regulatory compliance**: With the increasing emphasis on online transaction security and regulations such as PSD2/SCA in Europe, implementing 3D Secure helps merchants comply with these regulations and avoid potential fines and penalties. ## How 3D Secure works 3D Secure consists of several steps involving merchants, card networks, and card issuers to allow customers to be authenticated during online purchases. At a high level, a standard challenge flow looks like this. ![A visual flow diagram depicting the 3D Secure payment process in three steps, Payment initialized, Issuer Challenge and Process Payment.]() 1. **Payment initiation**: When a customer initiates an online purchase and enters their card details on the merchant's website, the merchant recognizes that the card is enrolled in 3D Secure and triggers the authentication process. 2. **Issuer challenge**: The customer is redirected to a web page hosted by their card issuer where the customer is asked to authenticate themselves. This can be done through various methods, such as entering a static password, a one-time password (OTP) sent over SMS, or using biometric authentication (e.g., fingerprint or facial recognition). 3. **Process payment**: If the authentication is successful, the merchant processes the authenticated transaction and the customer is redirected back to the merchant's website with a confirmation of their purchase. If the authentication fails or isn't completed, the transaction is typically declined, and the customer is informed of the failure. 3DS supports frictionless authentications as well, which require no interaction from the customer. A frictionless flow is initiated when the issuer determines that a transaction is low-risk, eliminating the need for additional verification steps. Frictionless authentications aren't always possible, but they provide a much smoother customer experience because there's no challenge displayed to the customer, meaning there's no action for them to take. ### 3DS with Evervault Although the 3DS protocol is known for being difficult to work with, we built our solution from scratch (and we run our own 3DS server). We did this to make our API straightforward to use. We don't wrap other solutions, and our API isn't just a one-to-one mapping of the 3DS protocol. We manage a lot of the complexity for you, and surface just the parts you need to configure and control 3DS the way you want to. Completing a 3DS authentication with Evervault takes three steps: 1. Create the session 2. Run the session on your frontend 3. Retrieve and forward authentication credentials as needed That last step is an important differentiator. Because our solution is standalone, you can use 3DS authentications with any downstream partner. This is especially important for integrating with multiple payment service providers (PSPs). You could integrate with each PSP and use their 3DS solutions, but there are some issues with that. You're limited on the configuration side to whatever the PSPs allow, and it makes for an inconsistent customer experience because each 3DS implementation is different. Evervault lets you configure your own 3DS flows, and then share the results with any PSP, and customers always see the same interface. ## Get started with 3D Secure To start, you need to create a [Sandbox app](/developers/sandbox). Sandbox apps allow you to test 3D Secure without affecting live data. When you're ready to go live, [contact our support team](mailto:support@evervault.com) to enable 3D Secure on your production app. > **Configuring acquirers** > > Acquirer details aren't required when testing in [Sandbox](/developers/sandbox) mode. However, you must [configure acquirer details](#configuring-acquirer-details) before moving to production. ### 1. Create a 3DS session Use the [create session](/api#createThreeDSSession) endpoint by providing the card (in encrypted form using our [card collection](/cards/card-collection), as a network token, or in plaintext), merchant, and payment details. Do this from your backend and then pass the session ID to your client code. #### Node ```javascript const response = await fetch("https://api.evervault.com/payments/3ds-sessions", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Basic " }, body: { card: { number: "4242424242424242", expiry: { month: "05", year: "25" } }, merchant: { name: "Ollivanders Wand Shop", website: "https://www.ollivanders.co.uk", categoryCode: "5945", country: "ie" }, payment: { type: "one-off", amount: 1000, currency: "eur" } } }) const session = await response.json(); [ANNOTATION lines=9-9] You can also provide an encrypted card number to prevent having plaintext card numbers in your infrastructure. [/ANNOTATION] ``` #### Ruby ```ruby response = HTTParty.post( "https://api.evervault.com/payments/3ds-sessions", headers: { "Content-Type" => "application/json", "Authorization" => "Basic " }, body: { card: { number: "4242424242424242", expiry: { month: "05", year: "25" } }, merchant: { name: "Ollivanders Wand Shop", website: "https://www.ollivanders.co.uk", categoryCode: "5945", country: "ie" }, payment: { type: "one-off", amount: 1000, currency: "eur" } }.to_json ) session = JSON.parse(response.body) [ANNOTATION lines=9-9] You can also provide an encrypted card number to prevent having plaintext card numbers in your infrastructure. [/ANNOTATION] ``` #### Python ```python import requests import json response = requests.post( "https://api.evervault.com/payments/3ds-sessions", headers={ "Content-Type": "application/json", "Authorization": "Basic " }, json={ "card": { "number": "4242424242424242", "expiry": { "month": "05", "year": "25" } }, "merchant": { "name": "Ollivanders Wand Shop", "website": "https://www.ollivanders.co.uk", "categoryCode": "5945", "country": "ie" }, "payment": { "type": "one-off", "amount": 1000, "currency": "eur" } } ) session = response.json() [ANNOTATION lines=12-12] You can also provide an encrypted card number to prevent having plaintext card numbers in your infrastructure. [/ANNOTATION] ``` #### PHP ```php $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.evervault.com/payments/3ds-sessions", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Basic {credentials}" ], CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ "card" => [ "number" => "4242424242424242", "expiry" => [ "month" => "05", "year" => "25" ] ], "merchant" => [ "name" => "Ollivanders Wand Shop", "website" => "https://www.ollivanders.co.uk", "categoryCode" => "5945", "country" => "ie" ], "payment" => [ "type" => "one-off", "amount" => 1000, "currency" => "eur" ] ]) ]); $response = curl_exec($curl); $session = json_decode($response, true); curl_close($curl); [ANNOTATION lines=13-13] You can also provide an encrypted card number to prevent having plaintext card numbers in your infrastructure. [/ANNOTATION] ``` #### Java ```java URL url = new URL("https://api.evervault.com/payments/3ds-sessions"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Authorization", "Basic "); conn.setDoOutput(true); String jsonInputString = "{" + "\"card\": {" + " \"number\": \"4242424242424242\"," + " \"expiry\": {" + " \"month\": \"05\"," + " \"year\": \"25\"" + " }" + "}," + "\"merchant\": {" + " \"name\": \"Ollivanders Wand Shop\"," + " \"website\": \"https://www.ollivanders.co.uk\"," + " \"categoryCode\": \"5945\"," + " \"country\": \"ie\"" + "}," + "\"payment\": {" + " \"type\": \"one-off\"," + " \"amount\": 1000," + " \"currency\": \"eur\"" + "}" + "}"; try(OutputStream os = conn.getOutputStream()) { byte[] input = jsonInputString.getBytes("utf-8"); os.write(input, 0, input.length); } try(BufferedReader br = new BufferedReader( new InputStreamReader(conn.getInputStream(), "utf-8"))) { StringBuilder response = new StringBuilder(); String responseLine = null; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } JSONObject session = new JSONObject(response.toString()); } [ANNOTATION lines=12-12] You can also provide an encrypted card number to prevent having plaintext card numbers in your infrastructure. [/ANNOTATION] ``` #### Go ```go client := &http.Client{} jsonData := map[string]interface{}{ "card": map[string]interface{}{ "number": "4242424242424242", "expiry": map[string]interface{}{ "month": "05", "year": "25", }, }, "merchant": map[string]interface{}{ "name": "Ollivanders Wand Shop", "website": "https://www.ollivanders.co.uk", "categoryCode": "5945", "country": "ie", }, "payment": map[string]interface{}{ "type": "one-off", "amount": 1000, "currency": "eur", }, } jsonValue, _ := json.Marshal(jsonData) req, _ := http.NewRequest("POST", "https://api.evervault.com/payments/3ds-sessions", bytes.NewBuffer(jsonValue)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic ") resp, _ := client.Do(req) defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) var session map[string]interface{} json.Unmarshal(body, &session) [ANNOTATION lines=4-4] You can also provide an encrypted card number to prevent having plaintext card numbers in your infrastructure. [/ANNOTATION] ``` #### 3DS Requestor Initiated (3RI) transactions Merchant-initiated transactions (MIT) are commonly used to charge customers for subscriptions, installment payments, etc. 3RIs are a form of MIT that include a 3DS authentication without the cardholder being present (flows are frictionless and either succeed or fail, there's no challenge involved). These 3RIs are often used instead of standard MITs because they can improve acceptance rates and may still result in liability shift (moving chargeback liability from you to the issuer). To use 3RIs, you still complete initial 3DS sessions with customers. You keep track of the session ID, and then you use it later to create a 3RI which carries over the authentication. To create a 3RI transaction, set the: - [Initiator type](/api#createThreeDSSession-request-initiator) to `merchant` - [initialSession](/api#createThreeDSSession-request-initiator-initialsession) to the [ID returned from the original session](/api#createThreeDSSession-response-initiator-initialsession) or a `threeDSServerTransactionID` - [Payment type](/api#createThreeDSSession-request-payment) to `recurring` or `installment` #### Visa Data Only authentication Visa's Digital Commerce Authentication Program (DCAP) lowers interchange fees on transactions authenticated through Visa's Data Only flow. Rather than challenging the cardholder, Data Only sends enriched transaction data to Visa for risk assessment. The customer sees no challenge, and you pay a lower interchange rate. Like 3RIs, a Data Only session authenticates as soon as it's created, so you don't mount the SDK on your frontend. Collect the extra data points along with the usual 3DS data, and Evervault authenticates the session when you create it. To create a Data Only session for a Visa card, set the [challenge preference](/api#createThreeDSSession-request-challenge) to `no-challenge-requested` and the [reason](/api#createThreeDSSession-request-challenge-reason) to `data-sharing`, and supply all of the following: - The customer's full [billing address](/api#createThreeDSSession-request-customer-billing), including `state` - The customer's [phone](/api#createThreeDSSession-request-customer-phone) and [email](/api#createThreeDSSession-request-customer-email) - A [deviceId](/api#createThreeDSSession-request-deviceid) you generate for the customer's device - The customer's [clientIp](/api#createThreeDSSession-request-clientip) > **State is required** > > `state` is optional on most 3DS sessions, but a Data Only session won't qualify without it. Supply it even for countries where you'd usually leave it out. Evervault reports the outcome on the `dcap` object, which is present on every Visa session created with a `data-sharing` reason. **The dcap object** - `qualified` `boolean` — Whether you supplied every data point the program requires, so the transaction was submitted to Visa for DCAP. - `accepted` `boolean` — Whether Visa reported your data as meeting the program's quality requirements. A session can be `qualified` but not `accepted` — for example, when the billing address and IP address resolve to different places. [Retrieve and forward the authentication credentials](#3-retrieve-and-forward-authentication-credentials) after creating the session, as you would for any other session. Evervault also recommends combining Data Only with [network tokens](/cards/network-tokens), which reduce interchange further. ### 2. Run the session on your frontend After creating the session, use our client-side SDKs to complete the authentication flow on your frontend. If for some reason you can't use our SDKs, you can try [using a redirect](#using-a-page-redirect) but Evervault doesn't generally recommend this (redirects add friction to the customer experience). #### Browser #### Install the SDK Our [JavaScript SDK](/sdks/javascript) is distributed from our CDN, and can be installed by placing this script tag in the head of your HTML file. The SDK must be loaded directly from our CDN and cannot be bundled with your application or self hosted. ```html ``` Once the SDK is installed, initialize it using your Team ID and App ID. You can find these in the [Evervault Dashboard](https://app.evervault.com). ```javascript const evervault = new Evervault("", ""); ``` You can also install Evervault via the `@evervault/js` package on npm. This package is a light wrapper which handles loading the SDK from our CDN and also provides TypeScript definitions. ```javascript import { loadEvervault } from "@evervault/js"; const evervault = loadEvervault("", ""); ``` #### Frequently Asked Questions ##### Why does the SDK need to be loaded from the CDN? The SDK must be loaded directly from our CDN in order to be PCI Compliant. ##### How do I get my Team ID and App ID? You can find your Team ID and App ID in the [Evervault Dashboard](https://app.evervault.com). ##### Can I load the SDK asynchronously? You can load the SDK asynchronously using the `async` attribute on the script tag to prevent blocking the loading of your page. However, it is important to note that you will need to wait for the SDK to load before making any API calls. ```html ``` #### Start the authentication The `ThreeDSecure` component can be initiated using the `evervault.ui.threeDSecure` method by passing the session ID obtained in the previous step. After you initialize the `ThreeDSecure` component, call the `.mount()` method to open the 3DS modal in the customer's browser to start the authentication process. The `success` event will be fired after the authentication process completes. The `failure` event will be fired if the authentication process fails (e.g., the customer failed to authenticate). These callbacks occur regardless of whether there was a challenge or if the flow was frictionless. ```javascript const evervault = new Evervault("{{TEAM_ID}}", "{{APP_ID}}"); const threeDSecure = evervault.ui.threeDSecure("tds_visa_5a9a7b0a574c"); threeDSecure.on("success", () => { // 3DS is complete and payment can be finalized }); threeDSecure.on("failure", () => { // 3DS failed. Try again. }); threeDSecure.mount(); ``` Learn more about the [JavaScript SDK](/sdks/javascript). #### React #### Install the SDK Our React SDK is distributed via npm and can be installed using your preferred package manager. Once installed, Initialize the SDK by wrapping your application with the `EvervaultProvider` component. ```jsx import { EvervaultProvider } from "@evervault/react"; export default function App() { return ( ... ); } ``` #### Start the authentication The `useThreeDSecure` hook returns a `ThreeDSecure` object you can use to run the authentication. After you initialize the hook, call the start method with the session ID from the previous step to start the authentication process. The `onSuccess` callback will be fired after the authentication process completes. The `onFailure` callback will be fired if the authentication process fails (e.g., the customer failed to authenticate). These callbacks occur regardless of whether there was a challenge or if the flow was frictionless. ```jsx filename='app.jsx' import { Card, useThreeDSecure } from "@evervault/react"; import { createThreeDSSession, finalizePayment } from "./api"; function Checkout() { const threeDSecure = useThreeDSecure(); const handlePay = async () => { const sessionId = await createThreeDSSession(); threeDSecure.start(sessionId, { onSuccess: () => { // 3DS is complete and payment can be finalized }, onFailure: () => { // 3DS failed. }, }); }; return ( <> ); } ``` Learn more about the [React SDK](/sdks/react). #### React Native #### Install the SDK Our React Native SDK is distributed via `npm` and can be installed using your preferred package manager. The [react-native-webview](https://www.npmjs.com/package/react-native-webview) package is a peer dependency and will need to be installed as well. Once installed, Initialize the SDK by wrapping your application with the `EvervaultProvider` component. ```jsx import { EvervaultProvider } from "@evervault/react-native"; export default function App() { return ( ... ); } ``` #### Start the authentication The `useThreeDSecure` hook returns a `ThreeDSecure` object you can use to run the authentication. After you initialize the hook, call the start method with the session ID from the previous step to start the authentication process. The `onSuccess` callback will be fired after the authentication process completes. The `onFailure` callback will be fired if the authentication process fails (e.g., the customer failed to authenticate). These callbacks occur regardless of whether there was a challenge or if the flow was frictionless. ```jsx import { ThreeDSecure, useThreeDSecure } from "@evervault/react-native"; import { createThreeDSSession } from "@your/api"; import { Button, Form, Modal } from "@your/ui"; function CustomCheckout() { const tds = useThreeDSecure(); async function handlePayment() { // Create a 3DS session on your backend const sessionId = await create3DSecureSession(); tds.start(sessionId, { onSuccess: () => { console.log("3DS successful"); }, onFailure: (error: Error) => { console.error("3DS failed", error); }, }); } return (
); } ``` Learn more about the [React Native SDK](/sdks/react-native). #### Frequently Asked Questions ##### Can I customize the styling for the challenge? The contents of the modal are controlled by the bank and can't be customized. ### 3. Retrieve and forward authentication credentials After completing the authentication, you can [retrieve](/api#retrieveThreeDSSession) information about the session as needed. To process the transaction and ensure liability is shifted, pass the raw Electronic Commerce Indicator (ECI) and 3DS cryptogram to your payment gateway or PSP. #### Node ```javascript const response = await fetch( "https://api.evervault.com/payments/3ds-sessions/tds_visa_5a9a7b0a574c", { headers: { Authorization: "Basic ", }, } ); const session = await response.json(); console.log(session.cryptogram); // MTIzNDU2Nzg5MDA5ODc2NTQzMjE= console.log(session.eci); // { "value": "05", "descriptor": "fully-authenticated", "liabilityShift": true } ``` #### Ruby ```ruby response = HTTParty.get('https://api.evervault.com/payments/3ds-sessions/tds_visa_5a9a7b0a574c', { headers: { 'Authorization': 'Basic ' } }) session = JSON.parse(response.body) puts session.cryptogram # MTIzNDU2Nzg5MDA5ODc2NTQzMjE= puts session.eci # { "value": "05", "descriptor": "fully-authenticated", "liabilityShift": true } ``` #### Python ```python import requests response = requests.get('https://api.evervault.com/payments/3ds-sessions/tds_visa_5a9a7b0a574c', headers={'Authorization': 'Basic '}) session = response.json() print(session.cryptogram) # MTIzNDU2Nzg5MDA5ODc2NTQzMjE= print(session.eci) # { "value": "05", "descriptor": "fully-authenticated", "liabilityShift": true } ``` #### PHP ```php $response = Http::get('https://api.evervault.com/payments/3ds-sessions/tds_visa_5a9a7b0a574c', [ 'headers' => [ 'Authorization' => 'Basic ' ] ]); $session = json_decode($response->body()); print($session->cryptogram); # MTIzNDU2Nzg5MDA5ODc2NTQzMjE= print($session->eci); # { "value": "05", "descriptor": "fully-authenticated", "liabilityShift": true } ``` #### Java ```java import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(new URI("https://api.evervault.com/payments/3ds-sessions/tds_visa_5a9a7b0a574c")) .header("Authorization", "Basic ") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); var session = JsonConvert.DeserializeObject>(response.Body); System.Console.WriteLine(session.cryptogram); // MTIzNDU2Nzg5MDA5ODc2NTQzMjE= System.Console.WriteLine(session.eci); // { "value": "05", "descriptor": "fully-authenticated", "liabilityShift": true } ``` #### Go ```go client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.evervault.com/payments/3ds-sessions/tds_visa_5a9a7b0a574c", nil) req.Header.Set("Authorization", "Basic ") resp, _ := client.Do(req) defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) var session map[string]interface{} json.Unmarshal(body, &session) fmt.Println(session.cryptogram) // MTIzNDU2Nzg5MDA5ODc2NTQzMjE= fmt.Println(session.eci) // { "value": "05", "descriptor": "fully-authenticated", "liabilityShift": true } ``` In some cases, downstream partners might also require one or more of these IDs: - [threeDSServer.transactionIdentifier](/api#retrieveThreeDSSession-response-threedsserver-transactionidentifier) - [directoryServer.transactionIdentifier](/api#retrieveThreeDSSession-response-directoryserver-transactionidentifier) - [accessControlServer.transactionIdentifier](/api#retrieveThreeDSSession-response-accesscontrolserver-transactionidentifier) These values are specific to the 3DS protocol, and not Evervault. They're sometimes requested in addition to the ECI and cryptogram, but not every provider requires them. ## Configuring acquirer details Acquirer details aren't required in [Sandbox](/developers/sandbox) mode but you do need them to use 3DS in production. Evervault automatically passes this information in when 3DS sessions are created, although you can [pass values in manually](#provide-acquirer-details-per-request) yourself. You can configure acquirers in the [dashboard](https://app.evervault.com/app/payments/acquirers) or with the [API](/api#createAcquirer), and you can set up default acquirers for each card network (Visa, Mastercard, etc.). When creating a 3DS session, Evervault uses the appropriate acquirer configuration based on the card network. Configurations are specific to each app, so you need to configure these for your staging and production apps separately. ### Acquirer info request template If you need to obtain acquirer information from one of your partners (e.g., PSPs), you can use the template message below to request it. It covers everything you need to configure acquirers within Evervault. ```md Hi, We're currently integrating Evervault as our 3D-Secure (3DS) Server provider and need to collect some information related to our merchant account to unblock our integration. Could you please provide the following details for each network we're set up with? There should be different acquirer BIN values for each of Visa, Mastercard, Discover, and American Express. - Merchant ID (MID): Our merchant account number/merchant ID. - Acquirer BIN: The numeric identifier you've received from each card network to represent the underlying acquiring bank. These should be 6-10 digits in length. - Merchant Name: The official name you have on file for us. - Merchant Category Code (MCC): Our four-digit business classification assigned by the card networks. Thank you! ``` ### Provide acquirer details per request Alternatively, you can provide acquirer details when creating each 3DS session by including the [acquirer object](/api#createThreeDSSession-request-acquirer) in your API request. This approach is useful if you work with multiple acquirers or need to specify different acquirer details for different transactions. However, supplying acquirer details per request adds complexity to your integration. You'll likely need to perform a BIN lookup to identify the card network, so that you can then decide which acquirer BIN or MID values to provide for each transaction. ### How Evervault resolves acquirer details When you create a 3DS session, Evervault follows this process to determine which acquirer configuration to use: 1. If your request includes an `acquirer` object, those details are used. 2. If your request includes an ID of an acquirer configuration in the `acquirer` field, that configuration is used. 3. If no acquirer details are provided in the request, Evervault uses the default acquirer configuration for the card's network, if available. 4. If no acquirer details can be resolved, session creation fails. ## Testing your implementation Provided you are using a Sandbox app, you can use specific test cards to simulate various real-life scenarios. These cards can be used in conjunction with any valid expiry date or CVC. | Number | Brand | | --- | --- | | **3D Secure challenge flow** | | | 4242 4242 4242 4242 | Visa | | 5555 5555 5555 4444 | Mastercard | | 3714 4963 5398 431 | American Express | | 6011 1111 1111 1117 | Discover | | 3622 720627 1667 | Diners Club | | **Successful frictionless flow** | | | 4111 1101 1663 8870 | Visa | | 5555 5501 3065 9057 | Mastercard | | 3782 8224 6310 005 | American Express | | 6011 0009 3838 5477 | Discover | | 3622 720638 3827 | Diners Club | | **Failed frictionless flow** | | | 4111 1117 3897 3695 | Visa | | 5555 5504 8784 7545 | Mastercard | | 3782 8224 6310 013 | American Express | | 6011 0009 3838 3100 | Discover | | 3622 720638 3835 | Diners Club | | **Attempted authentication** | | | 4111 1101 4848 6405 | Visa | | 5555 5588 2481 5604 | Mastercard | | 3782 8224 6310 021 | American Express | | 6011 0009 3838 7572 | Discover | | 3622 720638 3850 | Diners Club | | **Card not enrolled** | | | 4111 1101 4848 6413 | Visa | | 5555 5588 2481 5612 | Mastercard | | 3782 8224 6310 047 | American Express | | 6011 0009 3838 6491 | Discover | | 3622 720638 3868 | Diners Club | ## Alternative configurations These alternative configurations mostly relate to using redirects and the `failOnChallenge` flag. Both have somewhat niche use cases but can be useful in the right situations. ### Using failOnChallenge 3DS supports frictionless and challenge flows. Frictionless authentication requires no interaction from the customer (and maintains liability shift). The frictionless flow automatically occurs when the issuer determines that a transaction is low-risk, eliminating the need for additional verification steps. This is a much smoother customer experience. In challenge flows, customers are required to complete an authentication step to verify their identity. This is higher friction and usually requires a password, biometric authentication, etc. The issuer decides on the flow, but Evervault provides an additional flag that can reduce friction when a challenge is requested. When you create 3DS sessions, you can set `failOnChallenge` to `true`. If a challenge is requested, Evervault automatically fails the 3DS session which prevents the challenge from ever being shown to the customer. This isn't technically a frictionless flow in terms of 3DS, but it does allow you to skip the challenge. #### Browser ```javascript const tds = evervault.ui.threeDSecure("tds_visa_5a9a7b0a574c", { failOnChallenge: true, }); ``` #### React ```jsx const threeDSecure = useThreeDSecure({ failOnChallenge: true, }); ``` > **EU transactions** > > The challenge flow is required for EU transactions and as a result > frictionless-only 3D Secure isn't supported by banks in the EU. ### Using a page redirect The 3D Secure challenge can also be loaded directly—either through a full page redirect or served within an `