# Entri Activate Configuration Source: https://developers.entri.com/activate/configuration Configuration reference for the activateDomain() call. ## Overview `entri.activateDomain(config)` takes a single config object made up entirely of the existing [Connect](/connect/configuration) and [Sell](/sell/overview) keys. There are no Activate-specific keys. Any key you already pass to `showEntri()` or `purchaseDomain()` is valid here, and Entri forwards the relevant ones into whichever flow the user selects. This means you don't redefine your setup for Activate. Your authentication, DNS records, and branding carry over from the flows you already use. ## How the config is routed When the user picks "Connect a domain," Entri applies the Connect-side keys (the same ones `showEntri()` accepts). When the user picks "Buy a new domain," Entri applies the Sell-side keys (the same ones `purchaseDomain()` accepts). Shared keys such as your application credentials and branding apply to both paths. Both paths are always presented. There is no key to show only "buy" or only "connect." A partner who needs a single path should call `showEntri()` or `purchaseDomain()` directly instead of `activateDomain()`. ## Shared keys These keys behave the same way they do in the Connect and Sell flows. See the [Connect configuration reference](/connect/configuration) for the full list and field-level detail. | Key | Type | Description | | --------------- | -------- | -------------------------------------------------------------------- | | `applicationId` | `string` | Your Entri application ID. | | `token` | `string` | Short-lived JWT generated by your backend. | | `dnsRecords` | `array` | The records Entri applies once the domain is connected or purchased. | ## New UI and white-label Entri Activate is built on Entri's new UI. The unified modal, including the selection screen where the user chooses to buy or connect a domain, is themed through the same `whiteLabel` object you already pass for Connect. There is no separate Activate theming object. The routed Connect flow uses this same UI, so the white-label properties that apply are the ones documented for the new Connect UI. That list is the single source of truth, so rather than repeat it here, refer to it directly: The full set of `whiteLabel` properties supported by the new UI ### Selection screen The selection screen, where the user chooses to buy or connect a domain, is themed by the same `whiteLabel` branding properties as the rest of the flow. Your `logo`, `logoBackgroundColor`, `removeLogoBorder`, `hideCompanyLogo`, and `hideEntriLogo` settings all apply here, and `hideCompanyName` switches the heading from "Add a domain to " to a neutral "Add a domain." These are the same properties documented in the new UI reference above, so there is nothing extra to configure for branding. The screen's text, including the heading, the subtitle, and the "Buy a new domain" and "Connect a domain" card labels, is provided and localized by Entri. It is not currently overridable through `customCopy`. One behavior property is specific to the Activate selection screen: Controls the transition when the user picks "Buy a new domain." When true, the modal expands in place and continues into the Sell flow. When false or unset, Entri swaps to the Sell flow with the default bottom slide transition. The "Connect a domain" path always continues in the same modal regardless of this setting. ## Example ```js theme={null} entri.activateDomain({ applicationId: "YOUR_APPLICATION_ID", token: "GENERATED_JWT", dnsRecords: [ // ...records applied after the user connects or purchases a domain ], }); ``` # Entri Activate Events Source: https://developers.entri.com/activate/events Browser events and webhook notifications fired during the Activate flow. ## Overview Entri Activate fires one new browser event for the path-selection step, then hands off to the Connect or Sell flow, which fires its existing events for the rest of the session. On the backend, Activate does not introduce a new webhook payload. The flow the user selects sends the same Connect or Sell webhooks you already handle. ## `onEntriPathSelected` Event When the user chooses between buying a new domain and connecting an existing one, Activate fires `onEntriPathSelected`. Use it to track which path your users take or to update your own UI before the underlying flow opens. #### Sample usage: ```JavaScript JavaScript theme={null} function handleOnEntriPathSelected(event) { console.log('onEntriPathSelected', event.detail); } window.addEventListener('onEntriPathSelected', handleOnEntriPathSelected, false); ``` #### Sample `event.detail` object response: ```JSON JSON theme={null} { "path": "connect", "domain": "example.com", "user": "user-123456" } ``` #### Event details object description | Name | Type | Description | | ------ | ------ | -------------------------------------------------------------------------------------- | | path | string | Which path the user chose: `"sell"` or `"connect"`. | | domain | string | Domain the user entered, if collected before selection. Can be null. | | user | string | The `userId` sent over the initial configuration. Helpful for mapping users vs. flows. | ## Flow events After the path is selected, all subsequent browser events come from the chosen flow: For the connect path, listen for the [Entri Connect events](/connect/events). For the sell path, listen for the [Entri Sell events](/sell/events). Because Activate reuses these events, existing listeners you wrote for Connect or Sell continue to work without changes. ## Webhooks Activate does not add a webhook payload of its own. The selected flow fires its standard webhooks, so the server-side notification you receive when a domain goes live is the same one Connect or Sell would send on its own. See the [webhooks overview](/webhooks-overview) for payload detail. # Migrating to Entri Activate Source: https://developers.entri.com/activate/migration Upgrade an existing Connect or Sell integration to the unified Activate flow. ## Overview This page is for partners who already integrate Entri Connect, Entri Sell, or both, and want to move to the single `activateDomain()` entry point. After migrating, you make one call and Entri presents the "buy a new domain" or "connect a domain" choice, then routes the user into the matching flow. You no longer maintain your own selection screen or the branching logic that decides which flow to open. If you only ever offer one path, you don't need to migrate. `showEntri()` and `purchaseDomain()` continue to work unchanged. ## Before you start Entri Activate is available to all partners, so there is no dashboard toggle or account flag to enable first. If your existing Connect or Sell integration works today, you already have everything you need to call `activateDomain()`. There are two ways to adopt Entri Activate. **Already an Entri customer? You can skip the migration entirely.** Entri can turn Activate on for your account with **no code change required**. Just ping your account executive and we will get you started. The rest of this page covers the other path: adopting `activateDomain()` in your own code for full control over configuration and events. Because the Activate config is a superset of the Connect and Sell configs, the keys you pass today remain valid. See the [configuration reference](/activate/configuration) for details. ## Migrating from a two-flow integration If you currently show your own buy-versus-connect screen and call `showEntri()` or `purchaseDomain()` depending on the user's choice, replace that logic with a single `activateDomain()` call. ```js theme={null} // Before: your UI decides, then you call one flow or the other if (userChoseToBuy) { entri.purchaseDomain(sellConfig); } else { entri.showEntri(connectConfig); } // After: Entri presents the choice and routes internally entri.activateDomain({ ...sharedConfig, // Connect and Sell keys both live in this one object }); ``` Once migrated, you can remove your selection UI and the conditional that branched between the two flows. ## Migrating from a single flow If you call only `showEntri()` or only `purchaseDomain()` today and now want to offer both paths, move your existing config into an `activateDomain()` call and add the keys for the second flow. There are no Activate-specific keys to learn. You combine the same Connect and Sell keys you already know into one object. For example, a Connect-only integration adding the buy path: ```js theme={null} // Before: connect-only, your existing showEntri() call entri.showEntri({ applicationId: "YOUR_APPLICATION_ID", token: "GENERATED_JWT", dnsRecords: [ // ...records applied after the user connects their domain ], }); // After: same Connect keys, plus the Sell keys for the buy path entri.activateDomain({ applicationId: "YOUR_APPLICATION_ID", token: "GENERATED_JWT", dnsRecords: [ // ...same records, applied whether the user connects or buys ], // ...add the Sell keys you would pass to purchaseDomain() }); ``` A Sell-only integration adding the connect path works the same way in reverse: keep your `purchaseDomain()` keys and add the Connect keys for the existing-domain path. ## Events after migration Existing event listeners keep working. The chosen flow fires the same Connect or Sell browser events and webhooks as before, and Activate adds one new `onEntriPathSelected` event for the selection step. See [Activate events](/activate/events). # Entri Activate Overview Source: https://developers.entri.com/activate/overview A single SDK call that lets users buy a new domain or connect one they already own. ## Overview Most SaaS products depend on a working custom domain to deliver their core value, whether that's a website, an email address, or a branded storefront. Getting there means the user either buys a domain or configures one they already own, and until that domain is live the user is blocked. They can't send email from their brand, serve their site, or use whatever your platform sold them. **Entri Activate** is the call that closes that loop. `entri.activateDomain(config)` presents a centralized modal where the user chooses to buy a new domain or connect an existing one, then routes them through the right Entri flow automatically. Domain activation is what literally happens when that flow completes: the domain goes from purchased or owned to fully functional inside your platform. Before Activate, offering both paths meant building your own selection UI, writing conditional logic to invoke the right flow, and managing two separate integrations. Activate removes that complexity. You make one call and Entri handles the path selection and the routing. ## How it works 1. You call `entri.activateDomain(config)` and Entri opens a centralized modal. 2. The user chooses between "Buy a new domain" and "Connect a domain." 3. Based on that choice, Entri internally routes to the matching flow: * **Buy a new domain** runs the Entri Sell flow (`purchaseDomain()`), which handles the purchase and auto-configures DNS. * **Connect a domain** runs the Entri Connect flow (`showEntri()`), which guides the user through DNS setup for the domain they already own. 4. Entri applies the records you defined and monitors propagation, just as it does for the underlying flows. ## When to use Entri Activate Use Activate when you want to offer both the purchase and connect paths through a single unified entry point, or when you want Entri to handle the path-selection experience on your behalf. If you only ever need one path, you can continue to call `showEntri()` or `purchaseDomain()` directly. Activate does not replace Connect or Sell as standalone products. It sits in front of them as a single front door for partners who need both. ## Key features **Single integration** replaces the selection UI and branching logic you would otherwise build yourself. One call to `activateDomain()` covers both the purchase and connect paths. **Centralized path selection** presents the "buy" and "connect" choice in an Entri-managed modal, so you never build or maintain that screen. The modal is built on Entri's [new UI](/connect/new-ui) and is themed with the same `whiteLabel` object as the rest of the flow. **Automatic routing** sends the user into the Entri Sell or Entri Connect flow based on what they pick, with no state to manage across the two. **Consistent configuration** lets you define your DNS records once and have them applied no matter which path the user takes, and **propagation monitoring** carries through from the underlying flow so you're notified when the domain goes live. ## Quick links Full activateDomain() config key reference Browser events and webhook notifications Upgrade an existing Connect or Sell integration The flows Activate routes into # DMARC Handling: Advanced Options Source: https://developers.entri.com/advanced-dmarc-options Advanced DMARC handling via advancedDmarcOptions — tag overrides, removals, inheritance, and conditional additions. The new `advancedDmarcOptions` feature allows you to configure your DMARC records with greater flexibility and precision. It supports tag overrides, tag removal, inheritance from root domain records, and adding missing tags. This feature ensures better management of DMARC configurations, allowing complex updates in a single request. ### Pre-existing DMARC Record Validation When configuring DMARC records, it's important to ensure that the `value` field accurately reflects the desired final DMARC configuration. If the existing DMARC record is invalid, overrides cannot be applied effectively. For example, if a DMARC record is missing a required policy (`p` tag) and the user attempts to update the `rua` tag, the system will not be able to modify the invalid record. In such cases, the `value` provided in the request will be used as-is. ### Feature Overview The `advancedDmarcOptions` object can be included in your DMARC record configuration. This object supports the following keys: * `inheritRootDmarc`: Inherit the DMARC settings from the root domain for subdomains. * `overrideTags`: Override specific tags in the existing DMARC record or add them if they do not exist. * `removeTags`: Remove specific tags from the DMARC record. * `addTagsIfNotExist`: Add tags to the DMARC record only if they are not already present, ensuring certain tags are included without overriding existing values. ### Example Payload Here’s an example of a DMARC record using `advancedDmarcOptions`: ```json theme={null} { "type": "TXT", "host": "whatever_host", "value": "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com", "ttl": 300, "advancedDmarcOptions": { "inheritRootDmarc": true, "overrideTags": { "p": "none", "rua": "mailto:dmarc-reports@example.com", "sp": "reject" }, "removeTags": ["pct"], "addTagsIfNotExist": { "rua": "mailto:dmarc_agg@vali.email" } } } ``` ### Key Features * **Root DMARC Inheritance**: Allows subdomains to inherit DMARC policies from the root domain when `inheritRootDmarc` is set to `true`. * **Override DMARC Tags**: Use the `overrideTags` object to modify existing DMARC tags. For example, change the policy from `none` to `quarantine` or update reporting email addresses. * **Remove DMARC Tags**: Use `removeTags` to remove unwanted tags such as `pct` (percentage). * **Add Tags If They Don’t Exist**: The `addTagsIfNotExist` key ensures that specific tags are added only if they are missing from the DMARC record. ### Example Use Cases 1. **Overriding Existing Tags** * **Scenario**: Change the DMARC policy for a domain from `none` to `quarantine`. * **Before**: `v=DMARC1; p=none; rua=mailto:reports@example.com` * **After**: `v=DMARC1; p=quarantine; rua=mailto:reports@example.com` **Payload**: ```json theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=none; rua=mailto:reports@example.com", "ttl": 3600, "advancedDmarcOptions": { "overrideTags": { "p": "quarantine" } } } ] } ``` 2. **Adding New Tags** * **Scenario**: Add a `pct=50` tag to an existing DMARC record. * **Before**: `v=DMARC1; p=reject; rua=mailto:reports@example.com` * **After**: `v=DMARC1; p=reject; pct=50; rua=mailto:reports@example.com` **Payload**: ```json theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=reject; rua=mailto:reports@example.com", "ttl": 3600, "advancedDmarcOptions": { "addTagsIfNotExist": { "pct": "50" } } } ] } ``` 3. **Removing Tags** * **Scenario**: Remove the `rua` (aggregate report) tag. * **Before**: `v=DMARC1; p=quarantine; ruf=mailto: forensic@example.com rua=mailto:reports@example.com` * **After**: `v=DMARC1; p=quarantine; ruf=mailto:forensic@example.com` **Payload**: ```json theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=quarantine; rua=mailto:reports@example.com", "ttl": 3600, "advancedDmarcOptions": { "removeTags": ["rua"] } } ] } ``` 4. **Inheriting Root Domain Policy** * **Scenario**: Apply the DMARC policy from a root domain to a subdomain. * **Before**: Subdomain has no DMARC policy. * **After**: Subdomain inherits the root domain’s policy. **Payload**: ```json theme={null} { "domain": "sub.example.com", "dnsRecords": [ { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=none", "ttl": 3600, "advancedDmarcOptions": { "inheritRootDmarc": true } } ] } ``` 5. **Add Tag Only if Not present** * **Scenario**: Ensure that a domain has a specific DMARC tag set up, but only if it doesn't already have one set. This example uses the `rua` tag, but the approach works for any DMARC tag. * **Before (Case 1)**: `v=DMARC1; p=none;` * **After (Case 1)**: `v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com` * **Before (Case 2)**: `v=DMARC1; p=none; rua=mailto:existing-reports@example.com` * **After (Case 2)**: `v=DMARC1; p=none; rua=mailto:existing-reports@example.com` (unchanged) **Payload**: ```json theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=none;", "ttl": 3600, "advancedDmarcOptions": { "addTagsIfNotExist": { "rua": "mailto:dmarc-reports@example.com" } } } ] } ``` 6. **Complex Configuration** * **Scenario**: Multiple changes including policy update, email change, and tag addition. * **Before**: `v=DMARC1; p=none; pct=100; adkim=s; rua=mailto:old@example.com` * **After**: `v=DMARC1; p=quarantine; pct=100; aspf=s; rua=mailto:new@example.com` **Payload**: ```json theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "TXT", "host": "_dmarc", "value": "v=DMARC1; p=none; pct=100; rua=mailto:old@example.com", "ttl": 3600, "advancedDmarcOptions": { "overrideTags": { "p": "quarantine", "rua": "mailto:new@example.com" }, "addTagsIfNotExist": { "aspf": "s" }, "removeTags": ["adkim"] } } ] } ``` # Connect Configuration Source: https://developers.entri.com/connect/configuration Full reference for the EntriConfig object passed to showEntri(). ## `entri.showEntri(config)` This method launches the Entri modal window. `config` is an object with the following properties: | Property | Type | Required? | Default | Description | | ------------------------ | -------------------------------------- | ----------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | applicationId | string | Yes | N/A | The ID of your application (set in the dashboard) | | dnsRecords | array of DNSRecord objects (see below) | Yes | N/A | The DNS records you wish to configure for your users (see below) | | token | string | Yes | N/A | A JSON web token. Must be fetched in each session, see [Token creation (JWT)](#token-creation-jwt). | | applicationUrl | string | Only when using [Secure](/secure/overview) or [Power](/power/overview) | N/A | Sets the applicationUrl for Power. This is the specific application URL that you would like to render on the customer's custom domain. Also known as an origin server or origin URL. | | applicationName | string | No | null | The company name that will be shown in the initial screen's welcome message. | | defaultSubdomain | string | No | "" | If you would like to pre fill the subdomain field with text then enter the desired sub domain here. | | enableDkim | boolean | No | false | If your application requires users to set up DKIM through their email provider (and your app does not send emails directly), enable this. This is common for applications that provide email automation using an external service like Google Workspace or Microsoft 365. (Enterprise tier) | | forceManualSetup | boolean | No | false | Forces Entri to use the manual setup flow. If set to true then the prefilledDomain becomes required. | | forceSubdomain | boolean | No | false | If your application requires subdomains, enable this. (Premium and Enterprise tiers) | | locale | string | No | en | To load Entri in a specific language. Supported languages include: en, es, pt, pt-br,pt-pt, fr, it, de, nl, pl, tr, ja, da, sv.The pt locale defaults to Brazilian portuguese (pt-br). | | hostRequired | boolean | No, unless you use the `{SUBDOMAIN}` variable | true | If the `{SUBDOMAIN}` variable is used but is null (no subdomain inputted by the user) then the `{SUBDOMAIN}` value will default to be [www](http://www). See [Dynamic configuration variables based on the user-inputted domain](/connect/dns-records#dynamic-configuration-variables). | | manualSetupDocumentation | string | No | "" | Where you currently provide documentation on how to set up DNS. If users are trying to set up their DNS manually and need help, we will send them to this page. e.g. \<[https://example.com/dns-setup\\>](https://example.com/dns-setup\\>). This link is disabled when using the propertydisableManualSetupDocumentationLink:true in the whiteLabel's configuration. | | iframeSandbox | boolean | No | false | When set to `true`, applies a `sandbox` attribute to the Entri iframe, restricting its capabilities to only: `allow-scripts`, `allow-same-origin`, `allow-forms`, `allow-popups`, and `allow-popups-to-escape-sandbox`. This adds an extra layer of security to the embedded iframe. | | overrideSPF | boolean | No | false | Forces the SPF record override in case there is a pre-existent one instead of merging it. This feature is not supported by Cloudflare, IONOS, Squarespace, and GoDaddy providers. | | power | boolean | No | false | Enables Entri [Power](/power/overview). Requires adoption of Entri Power. | | prefilledDomain | string | No, unless forceManualSetup is set to true | N/A | A domain to pre-fill if you've collected it before the Entri modal, e.g. example.com | | secureRootDomain | boolean | No | false | Creates a certificate for the root domain and points its A records to Entri secure servers. Requires adoption of [Entri Secure](/secure/overview). | | supportForSubdomains | boolean | No | true | If your application allows subdomains, enable this | | userId | string | No | N/A | A unique ID so that you can match Entri webhook events to the user. You can use stringfied jsons in order to add complex structured data. | | wwwRedirect | boolean | No | false | When feasible, Entri will automatically redirect site.com to [www.site.com](http://www.site.com) using the preferred method of the DNS provider. Certain providers prevent Entri from automatically setting this up. When that is the case, Entri shows a helper UI to the user that explains how to do this manually. | | validateDmarc | boolean | No | false | When set to `true` Entri will automatically check if there is an existing and valid DMARC record. If so, Entri will not update the DMARC record. | | validateCAA | boolean | No | false | When set to `true` Entri will check if there is an existing CAA present in the domain or not. If it is present, it will add the CAA record sent in the config, else not. | | checkConflicts | boolean | No | false | When `true`, Entri analyzes the DNS records provided in `dnsRecords` against the existing authoritative DNS configuration and reports any record that would require an override. Increases response time. See [Detecting Record Conflicts](/connect/sdk#detecting-record-conflicts-checkconflicts). | | monitor | boolean | No | false | When `true`, automatically registers the configured domain and records with [Entri Monitor](/monitor/overview) after the Connect flow completes, so record drift is reported back via webhooks. Requires a Monitor-enabled plan. | | searchType | string | No | `"classic"` | (Entri Sell, IONOS only) Selects the domain-search experience. Use `"classic"` for the standard keyword search, or `"ai"` to enable AI-generated domain suggestions. | ### Possible `showEntri()` Errors When any of the following critical errors occur, a console error will be logged, and the user will only see “Entri is misconfigured. Please contact support for assistance.” in the UI. | Error | Console Message | Reason | | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Domain Invalid or Unsupported TLD | "This domain is invalid or uses an unsupported TLD (unlisted )." | The domain provided doesn't pass validation (invalid TLD, not in ICANN list, or malformed). | | Missing applicationId | "Missing parameter: applicationId" | The configuration object does not include `applicationId`. | | Configuration Error | "Configuration error, please refer to the docs at [https://developers.goentri.com/docs](https://developers.goentri.com/docs)" | `forceManualSetup: true` was used without providing `prefilledDomain`. | ## Token creation (JWT) To launch the Entri modal window in a session, you’ll need to fetch a JWT using the `secret` key and `applicationId` provided by the Entri dashboard. For security reasons and in order to not expose your secret in the browser, please **make sure to fetch the JWT on the server-side of your application**. The JWT expires after 60 minutes. You can use any networking library you’d like. Here’s an example using javascript: ```JS JS theme={null} fetch('https://api.goentri.com/token', { method: 'POST', body: JSON.stringify({ // These values come from the Entri dashboard applicationId: "12345", secret: "12345-67890" }), }) .then(response => response.json()) .then(data => { console.log('Success:', data); // { "auth_token": "exampletoken..." } }) .catch((error) => { console.error('Error:', error); }); ``` ### Enhancing Integration Security To further enhance the security of your integration, you have the option to include the `domain`, `dnsRecords` or `product` in the JWT creation process. When these elements are added, Entri will validate them during execution, providing an additional layer of security. ``` POST https://api.goentri.com/token ``` #### Request body ```JSON JSON theme={null} { "applicationId": "{APPLICATION_ID}", "secret": "{SECRET}", "domain": "sampledomain.com", "freeDomain": true, "userId": "XYZ", "dnsRecords": [ { "value": "samplevalue.com", "host": "@", "ttl": 300, "type": "CNAME" } ] } ``` #### Parameters description | Parameter | Required? | Default | Description | | ------------- | --------- | ------- | --------------------------------------------------------------------- | | applicationId | Yes | N/A | The ID of your application (set in the dashboard) | | domain | Yes | N/A | The domain that will be configured | | secret | Yes | N/A | Client secret, can be found in the Dashboard | | dnsRecords | No | N/A | Records that will be configured on the `domain` | | freeDomain | No | N/A | Only applies to Entri Sell! Specifies a free or paid domain purchase. | | product | No | N/A | The `product` for which this JWT is being created for. | | userId | No | N/A | The `userId` from the user that will be using the flow. | #### Successful response (200 status) ```JSON JSON theme={null} { "auth_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...." } ``` For more details about how freeDomain is used in Entri Sell, refer to the [Advanced Security Settings for freeDomain](/sell/modal-integration#advanced-security-settings-for-freedomain) section. # Connect DNS Records Source: https://developers.entri.com/connect/dns-records Defining dnsRecords, supported types, and dynamic variables ({DOMAIN}, {SUBDOMAIN}, ...). ## `dnsRecords` object DNS records can be provided in two different ways, using a single array of records or an object with the array of records specified the domain and subdomain cases. ### Record object specification | Property | Type | Required? | Example | | -------------------- | ------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | host | string | Yes | www | | ttl | integer | Yes | 300 (This is the minimum accepted value. Some providers don't accept this value and will default to the minimum value allowed by the provider.) | | type | string (see below) | Yes | CNAME (Check the [Supported record types](#supported-record-types) section below) | | value | string | Yes | m.example.com | | fallbackValue | string | No | v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDg9/gR+3J0mmugLjhpYOfQK9ytkEKnXM0kVdpu0UoykSPK7ChD+nRxFJbN2cxtvu8GrCNQwPTKbC0jimaSi0V2j3JndnRrECuYCqeZYcRmw2lYs18QnmJRfCpweKoaGtv9zERCkeHwLcTaLkrSHrRDf58WgERg8x/4ipBPIyZufwIDAQAB | | priority | integer | Only for MX records | 10 | | advancedDmarcOptions | object | Only for DMARC (`TXT`) records | See the full schema in [DMARC Handling: Advanced Options](/advanced-dmarc-options). Lets you override, remove, or conditionally add DMARC tags, and inherit DMARC from the root domain. | ### `advancedDmarcOptions` (DMARC records only) For DMARC `TXT` records you can attach an `advancedDmarcOptions` object to a record to take precise control over how Entri merges, overrides, or preserves individual DMARC tags. | Property | Type | Description | | ------------------- | ------- | ----------------------------------------------------------------------------------------------------- | | `inheritRootDmarc` | boolean | When `true` on a subdomain DMARC record, inherits the DMARC policy from the root domain. | | `overrideTags` | object | Map of tag → value. Overrides the listed tags in the existing DMARC record, or adds them if missing. | | `removeTags` | array | List of DMARC tag names to remove from the existing record (e.g. `["pct"]`). | | `addTagsIfNotExist` | object | Map of tag → value. Adds each tag **only** if it is not already present on the existing DMARC record. | See [DMARC Handling: Advanced Options](/advanced-dmarc-options) for full examples and use cases. ### Simple array A single array of dns records. This is the easiest way to send Entri the set of records you want to configure for all on all domains with no subdomain/domain distinction. ``` { ..., dnsRecords: [ { type: "CNAME", host: "shop", value: "myapp.com", ttl: 300, }, { type: "TXT", host: "@", value: "123-example-txt-record", ttl: 300, } ] } ``` ### Dynamic configuration variables You can insert dynamic variables into the dnsRecords section of your configuration object using syntax. For example, if the user sets up `blog.example.com`, the following variables will be available: | Variable name | Example value | | -------------------- | ------------- | | `{DOMAIN}` | example.com | | `{SUBDOMAIN}` | blog | | `{SLD}` | example | | `{TLD}` | com | | `{emailProviderSPF}` | Google | *Please note, all the variables are* **case-sensitive** *and must be inputted in all upper-case.* ## Supported Record Types | Type | Description | | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | A | Holds the IP address of a domain. | | AAAA | The AAAA record is conceptually similar to the A record, but it allows you to specify the IPv6 address of the server, rather than the IPv4. | | CNAME | Forwards one domain or subdomain to another domain. Does **not** provide an IP address. | | CAA | Used to provide additional confirmation for the Certification Authority (CA) when validating an SSL certificate. | | MX | Directs email to an email server. | | TXT | Stores text strings. For special uses of the TXT record, see [Handling DKIM, SPF, and DMARC records](/handling-dkim-spf-dmarc-records). | | NS | Indicates which DNS server is authoritative for that domain, it is the server that stores all DNS records for a domain, including A records, MX records, or CNAME records. | # Connect Events Source: https://developers.entri.com/connect/events Browser events (onSuccess, onEntriClose, onEntriStepChange, onSharedFlowSent, onEntriManualSetupDocumentationClick, onEntriRequestClose) and Connect-scoped webhook events. This page documents the webhook events and payload fields relevant to Entri Connect. For the payload envelope and signature verification, see [Webhooks Overview](/webhooks-overview). ## Webhook event types * `"domain.added"`: Indicates that DNS records have been successfully added. * For **Entri Sell**, this means the domain has been registered and connected. * For **Entri Connect**, it confirms that DNS records have been added to an existing domain. * Multiple messages of this type may be received for the same domain. This occurs when either the propagation\_status or redirection\_status changes. * `"domain.propagation.timeout"`: This event is very rare and is triggered if DNS records fail to propagate after 72 hours. It is highly uncommon for domains connected through the automatic flow. * `"domain.flow.completed"`: Indicates that the user has completed the Entri domain connection flow. This event does not convey any propagation status — it only confirms that the user finished the flow steps. ## Status fields * **`propagation_status`**: The status of DNS record propagation. Possible values: * `"pending"`: DNS records are still being propagated. * `"success"`: DNS records have been successfully propagated. * `"failed"`: DNS record propagation has failed. This is triggered if the records are not propagated even after 72 hours. * `"exempt"`: Only present in notifications with `type:"purchase.confirmation.expired"`. * **`dkim_status`**: The status of DKIM (DomainKeys Identified Mail) configuration. Possible values: * `"pending"`: DKIM setup is pending. * `"success"`: DKIM configuration has been successfully completed. * `"failed"`: DKIM setup failed. * `"exempt"`: DKIM setup is exempt, usually if the user does not use a supported email provider (e.g. Google or Microsoft). * **`redirection_status`**: The status of domain redirection. Possible values: * `"pending"`: Redirection setup is in progress. * `"success"`: Redirection setup has completed successfully. * `"failed"`: Redirection setup failed. * `"exempt"`: Redirection is exempt from this setup. * **`setup_type`**: Indicates whether the setup was done automatically or manually. Possible values: * `"automatic"`: Setup was performed automatically. * `"manual"`: Setup required manual intervention. * `"async"`: Setup was done using the [DNS asynchronous update](/sell/sdk#asynchronous-dns-configurations-entri-sell-only). * `"api"`: The setup was performed using the **Entri Sell Enterprise API** ### Advanced DMARC configuration feature This field is present in the webhook when using the [DMARC Handling: Advanced Options](/advanced-dmarc-options) or `validateDmarc` features. * **`dmarc_updated`**: Reflects the status of DMARC (Domain-based Message Authentication, Reporting, and Conformance) updates. Possible values: * `"true"`: DMARC settings have been updated. * `"false"`: DMARC settings were not updated. * `"exempt"`: DMARC is not applicable in this situation (e.g. for domains that don’t require it). ## Data object (DNS records) The `data` object contains the DNS records that have been propagated and those that are pending. * **`records_propagated`**: An array of DNS records that have been successfully propagated. Each record contains: * **`type`**: The type of DNS record (e.g. `"A"`, `"CNAME"`). * **`host`**: The hostname for which the DNS record applies (e.g. `"smallbusiness.com"`). * **`value`**: The value of the DNS record (e.g. `"54.153.2.220"`). * **`applicationUrl`**: (optional, Entri Power only) The application URL tied to the propagated DNS record (e.g. `"www.example.com"`). * **`powerRootPathAccess`**: (optional, Entri Power only) An array of paths accessible through Entri Power. Example: ```json theme={null} ["path1", "path2", "pathN"] ``` * **`records_non_propagated`**: An array of DNS records that have not yet propagated. The structure is the same as `records_propagated`. ## Shared flows When a flow is initiated via a sharing link or with a `sharedFlowId`, webhook events include an `origin_flow_id` to correlate the derived flow back to the origin. See [Shared Links](/connect/shared-links#correlating-shared--derived-flows) for details. ## Frontend event: onSharedFlowSent When using shared flows in the frontend experience, Entri exposes an `onSharedFlowSent` callback that fires when a user copies a shared flow and reaches the shared-flow congratulations screen. This event includes the `sharedFlowId`, allowing you to bind: * the **sharedFlowId** (originating / shared flow) * the **derived flow run** that completes later (via `origin_flow_id` in webhooks) Example usage: ```js theme={null} entri.purchaseDomain({ // ... onSharedFlowSent: ({ sharedFlowId }) => { // Persist sharedFlowId to correlate with webhook origin_flow_id } }) ``` ## Browser callback events The following events are useful for following up the user's navigation and take action under certain circumstances, for example, leading the user to your manual setup guide in case you don't want to use Entri's. ### `onSuccess` Callback Event This event is triggered when the user reaches the **Congratulations** screen in Entri Connect and Entri Sell, regardless of whether the setup was **automatic** or **manual**. Use this event to capture a definitive success signal and the `jobId` that identifies the flow. #### Sample usage: ```JavaScript JavaScript theme={null} function handleOnSuccess(event) { console.log('onSuccess', event.detail); } window.addEventListener('onSuccess', handleOnSuccess, false); ``` #### Sample `event.detail` object response: ```JSON JSON theme={null} { "domain": "example.com", "success": true, "setupType": "automatic", "provider": "Provider name", "freeDomain": true, "jobId": "2132b20f-e522-42fc-9604-5a53b1f13a4b" } ``` #### Event details object description | Name | Type | Description | | ---------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | domain | string | Domain that was configured. | | success | boolean | Always `true` for this event, indicating the flow completed successfully. | | setupType | string | Type of DNS setup used: `automatic` or `manual`. | | provider | string | DNS provider used to configure the domain, or `unknown` if not detected. | | freeDomain | boolean | Only included for Entri Sell **free domain** flows; otherwise may be omitted. | | jobId | string (UUID) | The unique identifier for the Connect/Sell flow. It is returned by the **initialize** response and is also included in all related webhooks. | The `onSuccess` event complements existing browser events and is the recommended source to capture the `jobId` for subsequent server-side operations (e.g., retrieving the last webhook or triggering a propagation re-check). ### `onEntriClose` Callback Event This event gets triggered as soon as the modal is closed, giving you back useful information about the latest status of the flow. #### Sample usage: ```JavaScript JavaScript theme={null} function handleOnEntriClose(event) { console.log('onEntriClose', event.detail); } window.addEventListener('onEntriClose', handleOnEntriClose, false); ``` #### Sample `event.detail` object response: ```JSON JSON theme={null} { "domain": "example.com", "success": true, "setupType": "automatic", "provider": "Provider name", "lastStatus": "IN_PROGRESS", "freeDomain": true, // Only applies to Entri Sell free domain flows "error": { // Added to the event details if the user closed the app after an error screen. "code": "ProviderError", "title": "Social Login Not Supported", "details": "You are trying to login with Social Account, please try logging in from [PROVIDER_NAME] Account" } } ``` #### Event details object description | Name | Type | Description | | -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | domain | string | Domain that the user entered. Can be null if the user exits before entering a domain. | | success | boolean | Was the setup of the domain completed? | | setupType | string | Type of DNS setup. Can be "automatic", `manual`, `sharedLogin`, `purchase` or `null` if the user didn't reach the set up stage. Additionally, semiautomatic may be returned if the user selected a provider manually that supports automatic set up. | | provider | string | Provider name used to configure the domain or "unknown" if it was not detected. | | lastStatus | string | lastStatus can be used if you want more information about where the user dropped off. Find all possible values below. | | manualScreenDisabled | boolean | Determines if the modal was closed because the flow reached the Manual Screen and it was disabled using the whiteLabel.customProperties.manualConfiguration.disableScreen property. | | freeDomain | boolean | Determines if the flow was for an Entri Sell free domain flow. Not included on other types of flow. | | error | object | Added to the event details if the user closed the app after an error screen. | | error.code | string | Exit error code | | error.title | string | Error title shown to the end user on the error screen | | error.details | string | Error details shown to the end user on the error screen | Errors description available at the [Possible error codes](#possible-error-codes) section. #### `lastStatus` possible values | lastStatus | Description | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | | INITIAL | Initial screen for Entri Connect, Secure and Power. | | ENTER\_DOMAIN | Enter your domain screen in case there's no prefilledDomain | | DOMAIN\_ANALYSIS | The domain is under the DNS analysis | | DOMAIN\_SETUP | The domain is on the DNS configuration step | | IN\_PROGRESS | The user exited the Entri modal while the DNS setup was in progress. | | EXISTING\_RECORDS | The user exited the Entri modal when prompted if they want to override an existing set of DNS records. | | LOGIN | The user exited the Entri modal flow during the login process (when prompted for their login credentials) | | LOGIN\_2FA | The user exited the Entri modal flow during the login process (when prompted for 2FA verification) | | MANUAL\_CONFIGURATION | The user exited the Entri modal after manual DNS configuration instructions were presented. | | PROVIDER\_MANUAL\_SELECTION | The user entered the providers' list for manual selection | | EXIT\_WITH\_ERROR | The user exited the Entri modal after an error occurred. | | DKIM\_SETUP | The user exited the Entri modal after prompting | | FINISHED\_SUCCESSFULLY | The user has successfully configured their domain using the automatic flow. | | FINISHED\_SUCCESSFULLY\_MANUAL | The user has successfully configured their domain using the manual flow. | | FINISHED\_SUCCESSFULLY\_LINK\_SHARED | The user completed the flow by sharing a configuration link (shared‑flow experience) rather than configuring the DNS themselves. | ### `onEntriManualSetupDocumentationClick` Event This event is triggered when the user has clicked on the "Follow our step-by-step guide" link on the Manual configuration screen. It doesn't include any additional information within the `event.detail` object. This is useful for triggering proprietary javavascript based solutions when the user needs help with configuring the manual flow, e.g. opening support chats, etc. #### Sample usage: ```JavaScript JavaScript theme={null} function myCustomFunction(event) { //Custom code here e.g. open chat app } window.addEventListener('onEntriManualSetupDocumentationClick', myCustomFunction, false); ``` ### `onEntriStepChange` Event This event is triggered each time a different screen is shown within the Entri configuration process. It serves the purpose of offering additional user journey insights, making it useful for implementing listeners and triggering third-party tracking system events when necessary. #### Sample usage: ```JavaScript JavaScript theme={null} function myCustomFunction(event) { console.log('onEntriStepChange', event.detail); } window.addEventListener('onEntriStepChange', myCustomFunction, false); ``` #### Sample `event.detail` object response: ```JSON JSON theme={null} { "domain": "example.com", "provider": "Provider name", "step": "ENTER_DOMAIN", "user": "user-123456", "error": { // Added to the event details if the user closed the app after an error screen. "code": "ProviderError", "title": "Social Login Not Supported", "details": "You are trying to login with Social Account, please try logging in from [PROVIDER_NAME] Account" } } ``` #### Event details object description | Name | Type | Description | | ------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | domain | string | Domain that the user entered. Can be null if the user exits before entering a domain. | | provider | string | Provider name used to configure the domain or "unknown" if it was not detected. | | step | boolean | The current step the user is visualizing | | user | string | The userId sent over the initial configuration. Helpful for mapping users vs. flows. | | error | object | Added to the event details if the user closed the app after an error screen. | | error.code | string | Exit error code | | error.title | string | Error title shown to the end user on the error screen | | error.details | string | Error details shown to the end user on the error screen | | pendingDomains | array | (Multi-domain flows) Domains that still need to be configured after the current step. | | processedDomains | array | (Multi-domain flows) Domains that have already been processed (successfully or with error) in the current flow. | | conditionalRecords | array | DNS records that apply only conditionally for the current step — for example, records that depend on the selected provider or on the user's chosen subdomain. Each entry follows the same shape as `dnsRecords`. | Errors description available at the [Possible error codes](#possible-error-codes) section. #### `step` possible values | lastStatus | Description | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | | INITIAL | Initial screen for Entri Connect, Secure and Power. | | ENTER\_DOMAIN | Enter your domain screen in case there's no prefilledDomain | | DOMAIN\_ANALYSIS | The domain is under the DNS analysis | | DOMAIN\_SETUP | The domain is on the DNS configuration step | | IN\_PROGRESS | The user exited the Entri modal while the DNS setup was in progress. | | EXISTING\_RECORDS | The user exited the Entri modal when prompted if they want to override an existing set of DNS records. | | LOGIN | The user exited the Entri modal flow during the login process (when prompted for their login credentials) | | LOGIN\_2FA | The user exited the Entri modal flow during the login process (when prompted for 2FA verification) | | MANUAL\_CONFIGURATION | The user exited the Entri modal after manual DNS configuration instructions were presented. | | PROVIDER\_MANUAL\_SELECTION | The user entered the providers' list for manual selection | | EXIT\_WITH\_ERROR | The user exited the Entri modal after an error occurred. | | DKIM\_SETUP | The user exited the Entri modal after prompting | | FINISHED\_SUCCESSFULLY | The user has successfully configured their domain using the automatic flow. | | FINISHED\_SUCCESSFULLY\_MANUAL | The user has successfully configured their domain using the manual flow. | | FINISHED\_SUCCESSFULLY\_LINK\_SHARED | The user completed the flow by sharing a configuration link (shared‑flow experience) rather than configuring the DNS themselves. | ### `onEntriRequestClose` Event This event lets host applications run their own confirmation UI (or cleanup logic) when the user clicks the close (X) button, instead of Entri's built‑in "Are you sure you want to leave?" dialog. This event is **gated** by [`whiteLabel.customProperties.general.useCustomExitConfirmation`](/connect/white-label#whitelabel-customproperties-object). Set it to `true` to enable the event; otherwise Entri shows its own default exit confirmation dialog and this event does **not** fire. When the event fires, Entri **does not** close the modal automatically. After your own confirmation UI resolves, your app is responsible for calling [`entri.close()`](/connect/sdk#entri-close) to dismiss the widget. The regular `onEntriClose` event then fires through the normal close flow. #### Sample usage: ```JavaScript JavaScript theme={null} function handleRequestClose(event) { console.log('onEntriRequestClose', event.detail); // Show your own confirmation UI here. // If the user confirms exit, call entri.close(): // entri.close(); } window.addEventListener('onEntriRequestClose', handleRequestClose, false); ``` #### `event.detail` payload | Field | Type | Always present | Description | | ------------------ | --------- | ----------------- | --------------------------------------------------------------------------------------------------------- | | `step` | string | Yes | The current `EntriStatus` at the time the user requested to close. See the status table above. | | `pendingDomains` | string\[] | Multi-domain only | Domains still pending setup. Only included when the flow is configured for multiple domains. | | `processedDomains` | string\[] | Multi-domain only | Domains that have already been processed. Only included when the flow is configured for multiple domains. | ### Possible `error` codes Below is a list of potential Entri modal errors. As a note, this list may not be exhaustive as some will be errors directly from the provider. | Error message | Title | Description | | -------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | AccessDeniedError | Invalid Permissions | We’re sorry an error occurred when trying to set up your domain. Please try again. | | AuthCodeError | 2FA code invalid. | Invalid code | | EmailNotVerifiedError | Unverified email | You need to complete the email verification process in your provider account. | | InvalidCredentialsError | You’ve exceeded the maximum number of login attempts | Please reset your password or set up your domain manually. | | InvalidDomainError | Domain not found | We found the domain in your `{provider}` account. However, your nameservers are pointed elsewhere. This means `{provider}` does not manage your DNS. Please try again with the service that manages your DNS settings. | | InvalidNameservers | Domain not found | We couldn’t find the domain you entered in your provider account. | | GenericError | An error occurred when trying to set up your domain | We’re sorry an error occurred when trying to set up your domain. Please try again. | | PurchaseDomainError | An error occurred when trying to set up your purchase. | Please refresh the page and try again. | | RegistroDomainInTransition | Domain in transition | Registro.br is transitioning your account to advanced DNS mode. This can take up to 5 minutes. Please try again in 5 minutes. | | SessionError | Session Error | Your session has timed out. Please refresh the page and try again. | | SpfRecordsLimitError | An error occurred when trying to set up your domain | SPF record cannot have more than 10 domains | | SpfRecordsLengthError | An error occurred when trying to set up your domain | SPF record cannot be longer than 255 characters | | UserInputTimeoutError | Your session has timed out | For your security, your session has timed out. Please start over. | | TimeoutError | An error occurred when trying to set up your domain | We're sorry, the process took more time than expected to complete. Please try again. | # Multi-Domain & Conditional Records Source: https://developers.entri.com/connect/multi-domain Configuring conditionalRecords, pendingDomains, and processedDomains for multi-domain flows. ## Conditional records object If you have different sets of records you need to configure depending on whether it’s a root domain, a subdomain, or you’re using custom name‑servers, then this is the best option for you. You just need to include three separate objects `rootNS`, `domain` and `subDomain` and the resolver will pick the right one: whenever the `rootNS` key is found it takes absolute precedence; if your provider doesn’t support NS modifications (or `rootNS` is absent), it falls back to `subDomain` for any subdomains, and finally to the `domain` records object. Sample conditional records object: ``` { ..., dnsRecords: { domain: [ { type: "A", host: "@", value: "1.2.3.4", ttl: 300, }, { type: "TXT", host: "@", value: "123-example-txt-record", ttl: 300, } ], subDomain: [ { type: "CNAME", host: "shop", value: "myapp.com", ttl: 300, }, { type: "TXT", host: "@", value: "123-example-txt-record", ttl: 300, } ], rootNS: [ { type: "NS", host: "@", value: "ns1.sampleprovider.com", ttl: 300, }, { type: "NS", host: "@", value: "ns2.sampleprovider.com", ttl: 300, } ] } ``` ## Multi-domain flows With Entri, you can configure as many domains as you need using the end user flow. In order to achieve it, you only need to use a similar `dnsRecords` structure as the conditional records, in combination with the array form of the `prefilledDomain` property. This is a sample configuration for a multi-domain flow: ``` { //... prefilledDomain: ["firstDomain.com", "secondDomain.com"], dnsRecords: { "firstDomain.com": [ { type: "A", host: "@", value: "1.2.3.4", ttl: 300, }, { type: "TXT", host: "@", value: "123-example-txt-record", ttl: 300, } ], "secondDomain.com": [ { type: "CNAME", host: "shop", value: "myapp.com", ttl: 300, }, { type: "TXT", host: "@", value: "123-example-txt-record", ttl: 300, } ] } ``` # Connect New UI Source: https://developers.entri.com/connect/new-ui White-label properties supported by the new UI used by Connect and Activate. This page documents the **new Entri UI**. It lists the `whiteLabel` properties the new UI supports. Everything below is configured under the `whiteLabel` object passed to [`entri.showEntri(config)`](/connect/sdk). [Entri Activate](/activate/overview) is built on this same new UI, so these properties also theme the Activate modal, including its selection screen. See [Activate configuration](/activate/configuration#new-ui-and-white-label) for the Activate-specific details. For the full (legacy) reference, see [Connect White Label](/connect/white-label). ## `whiteLabel` object The `whiteLabel` object lets you customize the Entri Connect UI to match your branding. All properties are optional — boolean flags default to `false`, and any property left unset falls back to Entri's default. You may need an Enterprise level account for some properties to be available; contact your account manager with questions. When true, hides all mentions of "Powered by Entri". Hides the customer's logo on the initial screen (does not apply to the multi-domain initial screen). Hides the company name across the flow. An `https` URL. When set, it overrides the configured Application logo and uses this one instead. We recommend an SVG or PNG that is 1:1. Sets a background color for the logo mask. Useful if your logo is white with a transparent background. When true, removes the rounded mask/border applied over your logo. When true, removes the sharing link functionality across the whole application. Skips the congratulations screen shown after a successful domain configuration. Colors, fonts, and dimensions. See [theme](#whitelabel-theme-object) below. Per-screen behavior toggles. See [customProperties](#whitelabel-customproperties-object) below. Text and copy overrides per locale. See [customCopy](#whitelabel-customcopy-object) below. ### `whiteLabel.theme` Object Customizes the colors, fonts, and dimensions of the modal to match your app's branding. Primary brand color (hex code). Applies to primary buttons and links. **Recommended for quick theming.** Text color on primary elements (hex code). Applies to text on buttons. **Recommended for quick theming.** Color scheme: `"disabled"` (light), `"enabled"` (dark), or `"auto"` (matches the system preference). Defaults to `disabled`. The modal's width. Background color of the modal container. Links color, to override HTML's default blue. Backdrop/overlay color. Use a transparent `hsla` or `rgba` value. The `border` CSS styling for buttons, e.g. `solid 1px #000`. The `border-radius` CSS styling for buttons, e.g. `5%` or `10px`. The `border-radius` CSS styling for input fields, e.g. `8px`. camelCase alias of `inputs.border.radius`. Overrides the modal `font-family` with a Google Font. The `font-family` normal text weight, e.g. `400`. Sets the modal `font-family` from a system font stack (no external font fetch). The `font-family` normal text weight, e.g. `400`. The `font-family` bold text weight, e.g. `600`. ### `whiteLabel.customProperties` Object Changes behaviors related to specific elements or a screen's behavior. A valid CSS selector (e.g. `#entri-container`) for the target element where the Entri Connect UI should be embedded. If null, the flow opens in a modal. Enables the go-to-manual link on the initial screen. Closes the modal and adds `authenticateManuallyClicked: true` to the `onEntriClose` event. Prevents the manual screen from being shown. Adds `manualScreenDisabled: true` to the `onEntriClose` event. Disables the Existing Records override prompt and confirms automatically. Displays the terms of service on the Provider Login screen. Hides the "Change provider" feature. Disables the Forward Link (forward-to-a-colleague) feature. Hides the "go to manual" text copy on the Provider Login screen. ### `whiteLabel.customCopy` Object Customizes texts along the flow to match your brand's tone or when the default copy doesn't fit your use case. Translations are keyed by locale; the `en` locale is required and all missing translations fall back to English. Sets the subtitle's translation text for the specific locale. Sets the Provider Login introduction's translation text for the specific locale. Accepts the `{PROVIDER}` replacement token. Supports `` for bold. Disables opening the documentation URL set in the `manualSetupDocumentation` configuration property. Commonly combined with the `onEntriManualSetupDocumentationClick` event to trigger a custom JS function. Sets the step-by-step-guide translation text for the specific locale. Supports `` tags for triggering JS functions (useful for chatbots): enclose the call-to-action text in `` and catch the `onEntriManualSetupDocumentationClick` event. Sets the Congratulations title's translation text for the specific locale. Accepts the `{DOMAIN}` replacement token. Supports `` for bold. Sets the Congratulations description's translation text for the specific locale. Accepts the `{USER}` and `{DOMAIN}` placeholders. Supports `` for bold. Sets the Congratulations button call-to-action's translation text for the specific locale. Supports `` for bold. Locales are codes such as `en`, `es`, `fr`, etc. If no matching translation is found for the locale in use, Entri's default text is used. See the full list of supported locales in the [configuration reference](/connect/configuration). ### White label code example ```json theme={null} { "whiteLabel": { "hideEntriLogo": true, "skipCongratulationsScreen": true, "theme": { "fg": "#FFFFFF", "bg": "#1F77F8", "darkMode": "auto", "systemFont": { "family": "Inter, system-ui, sans-serif", "variant": "400", "boldWeight": "600" } }, "customProperties": { "providerLogin": { "changeProvider": { "hide": true } } }, "customCopy": { "congratulations": { "title": { "en": "You're all set, {DOMAIN}!" } } } } } ``` # Entri Connect Overview Source: https://developers.entri.com/connect/overview Guided DNS setup for your users — what Entri Connect is and when to use it. ## Overview Setting up DNS records is one of the most common drop-off points in any SaaS onboarding flow. Users are asked to log into a registrar, find the right screen, and enter records correctly without making a mistake. Most can't do it without help. **Entri Connect** solves this with an embeddable modal that guides your users through the entire DNS configuration process automatically. Entri detects the user's DNS provider, logs in on their behalf where supported, and applies the records you define. No copy-pasting, no manual steps, no support tickets. Once a user completes the flow, Entri monitors propagation and fires a webhook when their records are live. ## How it works 1. Your backend generates a short-lived JWT and passes it to the frontend along with your DNS record configuration. 2. You call `entri.showEntri(config)` and the Entri modal opens inside your app. 3. Entri identifies the user's DNS provider from their domain. 4. For supported providers, Entri logs in automatically and applies the records on their behalf. 5. For unsupported providers, Entri shows step-by-step manual instructions tailored to that registrar. 6. Entri monitors propagation and notifies your server via webhook when records go live. ## When to use Entri Connect Use Connect when you need users to point a custom domain or subdomain to your service. It handles any combination of A, CNAME, TXT, MX, and other record types, and takes care of propagation monitoring so you don't have to build it yourself. ## Key features **Automatic configuration** supports 60+ DNS providers with direct API login. For providers outside that list, a **manual fallback** shows step-by-step instructions tailored to the specific registrar. **Propagation monitoring** fires webhook notifications when records go live or go missing. The modal is fully **white-labeled**: colors, logo, copy, and behavior are all customizable to match your brand. For more complex use cases, **multi-domain** lets users configure multiple domains in a single session, and **shared links** let them forward the configuration to a colleague who manages their DNS. Entri Connect's modal is being redesigned. The [new UI](/connect/new-ui) lists the `whiteLabel` properties it supports. The same UI also powers [Entri Activate](/activate/overview). ## Quick links showEntri(), checkDomain(), and all other methods Full showEntri() config key reference Theming, custom copy, and modal behavior Browser events and webhook notifications # Supported Providers Source: https://developers.entri.com/connect/provider-list Automatic configuration availability and wwwRedirect feature availability See the [provider health endpoint](/webhooks-overview#provider-health) for the realtime status of each provider ## Automatic DNS configuration support | Provider Name | Automatic | Social Login Supported | NS Support | Subdomain NS Support | | --------------------- | --------- | ---------------------- | ---------- | -------------------- | | 123-Reg | yes | no | yes | yes | | Alibaba Cloud | yes | N/A | yes | yes | | All-Inkl | yes | N/A | no | yes | | Amazon Route 53 (AWS) | yes | N/A | yes | yes | | Aruba | yes | N/A | yes | N/A | | Arsys | yes | N/A | yes | yes | | Bluehost US | yes | no | yes | yes | | Cloudflare | yes | yes | yes | yes | | ClouDNS | yes | N/A | yes | yes | | Crazy Domains | yes | no | yes | N/A | | Domain.com | yes | N/A | yes | yes | | DigitalOcean | yes | yes | yes | yes | | DNSimple | yes | yes | yes | yes | | DreamHost | yes | no | yes | yes | | Dynadot | yes | N/A | yes | yes | | EasyDNS | yes | N/A | yes | N/A | | Enom | yes | N/A | yes | N/A | | Fasthosts | yes | no | yes | N/A | | Freename | yes | no | yes | N/A | | Gandi | yes | N/A | yes | yes | | GoDaddy | yes | yes | no | yes | | GreenGeeks | yes | N/A | no | yes | | Hetzner | yes | N/A | yes | yes | | Home.pl | yes | N/A | yes | yes | | Hostgator | yes | N/A | yes | yes | | Hosting.com | yes | no | yes | N/A | | Hostinger | yes | no | yes | N/A | | Hostpoint | yes | N/A | no | yes | | Hover | yes | N/A | yes | N/A | | Inmotion Hosting | yes | N/A | yes | N/A | | IONOS | yes | yes | no | yes | | IWantMyName | yes | N/A | yes | yes | | LocaWeb | yes | N/A | no | no | | Mijndomein | yes | N/A | no | yes | | Name.com | yes | N/A | yes | yes | | Namebright | yes | N/A | yes | yes | | Namecheap | yes | N/A | yes | yes | | NameSilo | yes | N/A | yes | yes | | Network Solutions | yes | N/A | yes | yes | | Netlify | yes | no | N/A | yes | | O2switch | yes | N/A | no | N/A | | One.com | yes | N/A | yes | yes | | Openprovider | yes | N/A | yes | yes | | OpenSRS | yes | N/A | yes | yes | | OVH | yes | N/A | yes | yes | | Papaki | yes | no | yes | yes | | Porkbun | yes | N/A | yes | yes | | Register.com | yes | N/A | yes | yes | | Register.it | yes | N/A | yes | yes | | Registro.br | yes | N/A | N/A | N/A | | Shopify | yes | no | yes | N/A | | Simply | yes | no | N/A | yes | | SiteGround | yes | N/A | yes | N/A | | Spaceship | yes | N/A | yes | yes | | Squarespace | yes | yes | no | yes | | Strato | yes | N/A | no | N/A | | TransIP | yes | N/A | yes | yes | | United Domains | yes | N/A | yes | yes | | Vercel | yes | yes | no | yes | | Web.com | yes | N/A | yes | yes | | Wix | yes | no | N/A | N/A | | Wordpress.com | yes | no | yes | yes | | World4You | yes | N/A | no | yes | | Xneelo | yes | N/A | yes | N/A | ## wwwRedirect feature support | Provider Name | DNS Provider's native wwwRedirect support(Using provider's native features) | Entri's in-house wwwRedirect support(Using Entri Secure with https) | | --------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- | | 123-Reg | yes | yes | | Alibaba Cloud | yes | yes | | All-Inkl | yes | yes | | Amazon Route 53 (AWS) | no | yes | | Arsys | yes | yes | | Aruba | yes | yes | | Bluehost US | No | yes | | Cloudflare | yes | yes | | ClouDNS | no | yes | | Crazy Domains | yes (if included in the user's plan) | yes | | Domain.com | no | yes | | DigitalOcean | yes | yes | | DNSimple | yes | yes | | DreamHost | yes | yes | | Dynadot | yes | yes | | EasyDNS | no | yes | | Enom | yes | yes | | Fasthosts | no | yes | | Freename | no | yes | | Gandi | yes | yes | | GoDaddy | no | yes | | GreenGeeks | no | yes | | Hetzner | no | yes | | Home.pl | no | yes | | Hostgator | yes | yes | | Hosting.com | yes | yes | | Hostinger | no | yes | | Hostpoint | no | yes | | Hover | yes | yes | | Inmotion Hosting | yes | yes | | IONOS | yes | yes | | IWantMyName | yes | yes | | LocaWeb | no | yes | | Mijndomein | yes | yes | | Name.com | yes | yes | | Namebright | yes | yes | | Namecheap | yes | yes | | NameSilo | yes | yes | | Netlify | yes | yes | | Network Solutions | yes (if included in the user's plan) | yes | | O2switch | yes (if user has hosting plan) | yes | | One.com | yes | yes | | Openprovider | yes | yes | | OpenSRS | yes | yes | | OVH | yes | yes | | Papaki | no | yes | | Porkbun | yes | yes | | Register.com | yes (if included in the user's plan) | yes | | Register.it | yes | yes | | Registro.br | no | yes | | Shopify | no | yes | | Simply | yes | yes | | SiteGround | yes | yes | | Spaceship | yes | yes | | Squarespace | yes | yes | | Strato | yes | yes | | TransIP | no | yes | | United Domains | no | yes | | Vercel | no | yes | | Web.com | yes (if included in the user's plan) | yes | | Wix | yes | yes | | Wordpress.com | no | yes | | World4You | yes | yes | | Xneelo | no | yes | **`wwwRedirect` behavior varies by provider** Setting `wwwRedirect: true` instructs Entri to configure a root-domain redirect (`yourdomain.com` → `www.yourdomain.com`). This operates on the **root domain**, not the subdomain you may be configuring, and the exact mechanism used differs between DNS providers. Some providers (like GoDaddy) may override an existing A record at the root domain with redirect infrastructure. If a live site is being served on that root domain, it may become inaccessible due to the redirection — so make sure your site also loads on the `www` subdomain before enabling this.
**✓ Do:** Use `wwwRedirect: true` only when your explicit goal is to redirect the bare domain to the `www` subdomain — and when no active site depends on the root A record. **✕ Do not:** Use `wwwRedirect: true` when you're only trying to configure other subdomain CNAMEs (e.g. `shop.yourdomain.com`, `app.yourdomain.com`). The redirect flag has no effect on subdomains — it will only risk overwriting unrelated root-level records.
Any use of registered trademarks from DNS Providers is only used as necessary to clearly identify the relevant services and is not intended to imply or suggest any affiliation, sponsorship, endorsement, or business relationship. Entri LLC is not affiliated with or sponsored by GoDaddy. “GoDaddy” is a registered trademark of Go Daddy Operating Company, LLC. Use of registered trademarks is only used as necessary to clearly identify the relevant services and is not intended to imply or suggest any affiliation, sponsorship, endorsement, or business relationship with GoDaddy.com, LLC or its affiliated entities. # Connect SDK Source: https://developers.entri.com/connect/sdk The Entri Connect JavaScript SDK: showEntri(), checkDomain(), checkRecords(), load(), loadSharedFlow(), close(), and browser events. ## `entri.showEntri(config)` This method launches the Entri modal window. `config` is an object — see [Connect Configuration](/connect/configuration) for the full list of properties and the `showEntri()` error reference. ## `entri.checkDomain(domain, config)` This asynchronous method checks the DNS configuration for a specified domain and returns details about the current DNS provider — including the registrar, supported DNS features, and, if NS records are passed, the authoritative DNS provider managing the zone file. Use it to determine whether Entri supports automatic setup for a domain. It accepts two arguments: | Argument | Type | Required? | Example | | --------------------- | ----------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | domain | string - a domain with no subdomain | **Yes** | example.com | | config | Entri configuration object | **Yes** | See [Connect Configuration](/connect/configuration) | | config.checkConflicts | Boolean | **No** | When checkConflicts is set to `true`, the system analyzes the DNS records provided in the dnsRecords array and determine whether any of them would require an override. The default value is `false`. | **Important note**: `checkDomain` function also requires you to provide the JWT authentication key within the configuration object. Refer to [JWT authentication section](/connect/configuration#token-creation-jwt) for more information. ### Sample usage: ```js theme={null} const domainCheck = await entri.checkDomain("mydomain.com", { "applicationId": "myAppId", "token": "..." "dnsRecords": [ { "host": "www", "ttl": 300, "type": "CNAME", "value": "cname.mydomain.com" }, "checkConflicts": true, // Optional - Increases the response time //... etc ], //...other optional configuration keys } ) ``` ### HTTP endpoint **Premium tier and above.** You can also analyse the support for a domain with our http version of the `checkDomain` function. ```txt theme={null} POST https://api.goentri.com/checkdomain Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "ifpiggiescouldfly.com", "dnsRecords": [ { "host": "shop", "type": "CNAME", "value": "exampledestination.com", "ttl": "300" } ], "checkConflicts": true, // Optional - Increases the response time //... All the rest of the Entri Connect's configuration object } ``` #### Sample Response ```json theme={null} { "provider": "Cloudflare", "setupType": "Automatic", "NSSupport": { "root": true, "subdomains": true }, "wwwRedirect": true, "cnameFlattening": true, "wildcardSupport": true, "subdomainCnameNsOverride": false, "spfOverrideSupport": false, "caaSupport": true, "supportsSocialLogin": "yes", "expired": false, "registered": true } ``` For full detail, please refer to [Response reference](#response-reference) ### Detecting Record Conflicts (`checkConflicts`) When using the `checkDomain` function or endpoint, you can optionally enable DNS record conflict detection by adding the `checkConflicts` key to your configuration object. ```js theme={null} checkConflicts: true // Optional — default is false ``` When enabled, Entri analyzes all DNS records passed in the `dnsRecords` array and compares them against the existing authoritative DNS configuration of the domain.\ This helps you understand whether applying your desired configuration would require overwriting existing records. #### How conflict detection works If `checkConflicts` is set to `true`, Entri will: 1. Fetch the current DNS records for the relevant hostnames. 2. Compare them with the requested records. 3. Determine whether any requested record would overwrite existing DNS data. A conflict is detected when: * A record with the same **host** and **type** already exists with a different value. * A CNAME conflicts with other record types on the same hostname. * Any record conflicts with an existing CNAME. * MX records differ from the existing set. * SPF TXT records would require merging or replacement. * An NS record subdelegates a hostname where other records are being added. #### Response shape when `checkConflicts` is enabled If enabled, the `checkDomain` response will include a new field: ```json theme={null} "recordConflicts": null | [ ... ] ``` * `null` → No conflicts detected * `array` → A list of detected conflicts, including details about the existing and requested records Each conflict object follows this structure: ```json theme={null} { "host": "www", "type": "CNAME", "value": "www.exampledomain.com", // "priority": "1" // Only for MX records } ``` #### Example ```json theme={null} { "provider": "Cloudflare", "setupType": "Automatic", "recordConflicts": [ { "host": "www", "type": "CNAME", "value": "www.exampledomain.com", }, { "host": "@", "type": "MX", "value": "mail.exampledomain.com", "priority": 1 }, ], "expired": false, "registered": true } ``` #### Notes * Enabling `checkConflicts` increases response time because authoritative DNS lookups must be performed. * The data used for the analysis is gotten from the public domain DNS information. * If `checkConflicts: true` is provided **without** `dnsRecords`, the API will return an error. * This feature is designed to help partners determine whether prompting users about DNS overrides is necessary before running the automated setup. ### Response reference This section describes the response structure for the API endpoint or JavaScript function that checks the DNS provider information for a given domain. Depending on the DNS records passed as parameters, the response will include details about the domain's registrar or the authoritative DNS provider currently managing the zone file. #### Example 1: Standard DNS Records (No NS Records) When the request contains DNS records that are not NS (Name Server) records, the response will include information about the current DNS provider, such as the provider name, setup type, and feature support details. **Request:** ```json theme={null} { "applicationId": "myAppId", //Only JS function "token": "...", //Only JS function "domain": "entri.com", "dnsRecords": [ { "host": "shop", "type": "CNAME", "value": "exampledestination.com", "ttl": "300" } ] } ``` **Response:** ```json theme={null} { "provider": "Cloudflare", "setupType": "Automatic", "NSSupport": { "root": true, "subdomains": true }, "wwwRedirect": true, "cnameFlattening": true, "wildcardSupport": true, "subdomainCnameNsOverride": false, "spfOverrideSupport": false, "caaSupport": true, "supportsSocialLogin": "yes", "expired": false, "registered": true } ``` In this case, the `authoritativeDnsProvider` field is not included because NS records were not part of the request. #### Example 2: Request with NS Records If NS records are provided in the request, the response will also include information about the authoritative DNS provider managing the zone file. **Request:** ```json theme={null} { "applicationId": "myAppId", //Only JS function "token": "...", //Only JS function "domain": "entri.com", "dnsRecords": [ { "host": "@", "type": "NS", "value": "ns1.exampledestination.com", "ttl": "300" } ] } ``` **Response:** ```json theme={null} { "provider": "Cloudflare", "setupType": "Automatic", "NSSupport": { "root": true, "subdomains": true }, "wwwRedirect": true, "cnameFlattening": true, "wildcardSupport": true, "subdomainCnameNsOverride": false, "spfOverrideSupport": false, "caaSupport": true, "supportsSocialLogin": "yes", "authoritativeDnsProvider": { "provider": "Cloudflare", "setupType": "Automatic", "wwwRedirect": true, "cnameFlattening": true, "wildcardSupport": true, "subdomainCnameNsOverride": false, "spfOverrideSupport": false, "caaSupport": true, "supportsSocialLogin": "yes" }, "expired": false, "registered": true } ``` In this case, the `authoritativeDnsProvider` object is included because NS records were passed, providing details about the provider managing the zone file. ### Response Parameters * **provider**: The current DNS provider for the domain, either based on common DNS records or NS records if provided. * **setupType**: Indicates whether automatic setup is supported for the DNS provider. If not, a manual setup flow will be presented. * **NSSupport**: Object that specifies whether the provider supports nameserver configurations at the root or subdomain levels: * **root**: `true` if root-level nameserver configuration is supported. * **subdomains**: `true` if subdomain-level nameserver configuration is supported. * **wwwRedirect**: Indicates whether the provider supports automatic root domain-to-www redirects. * **cnameFlattening**: Specifies whether the provider supports CNAME flattening (resolving CNAMEs at the root level). * **wildcardSupport**: Indicates if wildcard DNS entries (e.g., `*.domain.com`) are supported by the provider. * **subdomainCnameNsOverride**: If `true`, the provider will automatically remove a CNAME record when a subdomain nameserver with the same name is added. * **spfOverrideSupport**: Determines how Entri handles SPF record updates. `true` if Entri can completely replace the existing SPF record with a new value. `false` if Entri can only append new values to the existing record or create a new one if no SPF record is present. * **caaSupport**: Determines wether the provider supports CAA records or not. * **supportsSocialLogin**: Specifies whether Entri supports social network logins for the provider. `"yes"` if **Entri** supports social login for this provider. `"no"` if **Entri** does not support social login for this provider. `"n/a"` if the **provider** does not support social login, so this setting does not apply. * **expired**: Boolean value indicating if the domain's current DNS setup has expired. * **registered**: Boolean value indicating whether the domain is registered. * **authoritativeDnsProvider** (only included when NS records are provided in the request): * *All the same fields as the above, but apply only to the `authoritativeDnsProvider`* This response format helps users understand the current DNS configuration and determine whether the provider supports certain advanced features like wildcard records, CNAME flattening, and subdomain overrides. ## `entri.checkRecords(domain, config)` This asynchronous method checks whether the DNS records you wish to configure are already present and correctly resolving on a given domain. Use it to determine whether an automated flow is still needed, or to build your own status UI on top of Entri. | Argument | Type | Required? | Example | | -------- | -------------------------- | --------- | --------------------------------------------------- | | domain | string | **Yes** | `example.com` | | config | Entri configuration object | **Yes** | See [Connect Configuration](/connect/configuration) | The configuration object must include `applicationId`, `token`, and the `dnsRecords` array to be verified. ### Sample usage: ```js theme={null} const result = await entri.checkRecords("mydomain.com", { applicationId: "myAppId", token: "...", dnsRecords: [ { host: "www", ttl: 300, type: "CNAME", value: "cname.mydomain.com" } ] }); ``` The response reports, per record, whether it is already propagated or still missing. ## `entri.close()` This method will force closing the modal and also trigger the onEntriClose() event with the last step details. Please refer to the [onEntriClose](/connect/events#onentriclose-callback-event) for more information. ## `entri.load()` This method on the [NPM package](https://www.npmjs.com/package/entrijs) loads the EntriJS script so it's ready for use when called. ## `entri.loadSharedFlow(url)` This method launches the Entri Connect modal using a shared‑link configuration. `entri.loadSharedFlow(url)` accepts a single parameter: | Argument | Type | Required? | Description | | -------- | ------ | --------- | ------------------------------------------------------------------------ | | `url` | string | Yes | A sharing‑link URL that contains a previously saved Entri configuration. | ### How it works 1. The function receives a sharing‑link URL. 2. Entri fetches the configuration stored within that sharing link. 3. Once retrieved, Entri automatically initializes the flow and opens the Entri Connect modal. 4. The experience is identical to calling `entri.showEntri(config)` with the same configuration object. ### Example usage ```js theme={null} entri.loadSharedFlow("https://example.com/shared/abc123"); ``` ### Notes * The shared‑link must contain a valid configuration generated by Entri's sharing‑link feature. * Any callbacks such as `onEntriClose` or `onSuccess` will behave the same as when using `entri.showEntri()`. * Errors related to invalid or expired shared links will surface in the console with the corresponding error code. # Shared Links Source: https://developers.entri.com/connect/shared-links Share setup links with end-users via loadSharedFlow(). ## Sharing Link Custom Domain **Enterprise tier only** Enterprise customers can replace the default `app.goentri.com` base URL in sharing links with a custom, branded domain. This creates a seamless white-labeled experience for the end users receiving and completing the shared flow. **Default sharing link:** ``` https://app.goentri.com/share/:shareId ``` **Custom domain sharing link:** ``` https://domains.yourbrand.com/share/:shareId ``` The custom domain serves the Entri sharing link experience transparently, so the path structure and behavior remain identical to the default. ### Configuration Configure your sharing link custom domain in the Entri Dashboard: 1. Go to **Account Settings → Application Settings** 2. Locate the **Sharing link URL** field 3. Enter your desired base URL (e.g., `https://domains.yourbrand.com`) 4. Save your changes Your custom domain must be set up to point to Entri. The required DNS configuration depends on whether you're using a subdomain or a root domain: * **Subdomain** (e.g., `domains.yourbrand.com`): add a `CNAME` record pointing to `sharing.goentri.com`. * **Root domain** (e.g., `yourbrand.com`): add the following `A` records (no `AAAA` records are required): * `15.197.212.204` * `3.33.229.73` Contact your account manager if you need assistance with the DNS configuration. Once configured, the `sharing_link_url` setting is automatically applied to all sharing links generated for that application — no code changes are needed on the frontend. ### Effect on the Sharing Links API When a custom domain is set, the `link` field returned by the [Sharing Links API](#sharing-links-api) will automatically reflect your custom domain instead of `app.goentri.com`: ```json theme={null} { "link": "https://domains.yourbrand.com/share/dd339cf05f2646539e79251f8e62f0c5", "job_id": "9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de" } ``` ### Using with `entri.loadSharedFlow()` Pass the full custom-domain URL directly to `entri.loadSharedFlow()` — no additional configuration is needed: ```js theme={null} entri.loadSharedFlow("https://domains.yourbrand.com/share/dd339cf05f2646539e79251f8e62f0c5"); ``` ## Webhook Notifications When your users go through an Entri flow, Entri sends webhook notifications to keep your backend informed about flow progress and outcomes. To learn more about webhook configuration and delivery, visit our [webhook documentation page](/webhooks-overview). ### Correlating Shared / Derived Flows Some platforms allow users to **share** a flow (for example, sending a link to another person). When that shared link is used, the resulting run is considered a **derived flow**. To help you correlate the derived flow back to the original one, Entri supports propagating an `origin_flow_id` value into webhook events. #### How it works * If a flow is initialized with `origin_flow_id` in its configuration, Entri stores that value on the created flow. * For **every webhook event** emitted for that flow, Entri includes the stored value as `origin_flow_id`. * If `sharedFlowId` was **not** provided when the flow was created, webhook payloads will **not** include `origin_flow_id`. Notes: * `origin_flow_id` is propagated **as-is** (no lookup, validation, or mutation). * This is a **non-breaking** change: existing webhook fields remain unchanged, and `origin_flow_id` is optional. #### Webhook payload example ```json theme={null} { "event": "domain.purchased", "flowId": "flow_123", "origin_flow_id": "flow_abc", "data": { "domain": "example.com", "status": "registered" } } ``` ### Shared flows and sharing link domains When a user completes an Entri flow that was initiated via a [sharing link](#sharing-links-api), Entri fires webhook events just like any standard flow. A few things to keep in mind: * The `job_id` returned when the sharing link was created is the identifier used to correlate all webhook events for that flow. It remains consistent regardless of whether a [custom sharing link domain](#sharing-link-custom-domain) is configured — the domain only affects the URL of the link itself, not the webhook payload. * To retrieve the last webhook event for a shared flow, use the [Get last webhook by job ID](/webhooks-overview#get-last-webhook-by-job-id) endpoint with the `job_id`. * If the flow was initiated with a `sharedFlowId`, webhook events for the derived flow will include an `origin_flow_id` field. See [Correlating Shared / Derived Flows](#correlating-shared--derived-flows) for details. ## Sharing links API The **Sharing Links API** allows users to securely hand off an ongoing Entri flow, such as Connect or Sell, to another person for completion. This is particularly useful when the current user does not have the required DNS credentials (in Entri Connect) or payment information (in Entri Sell). By generating a sharing link, you can let a colleague, client, or administrator pick up exactly where the flow was left off and complete the process without starting over. For Enterprise plan integrations, the sharing link URL can be white-labeled to use your own custom domain. See [Sharing Link Custom Domain](#sharing-link-custom-domain) for configuration details. ### Entri Connect ``` POST https://api.goentri.com/sharing/connect Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "applicationId":"yourApplicationID", "config":{ "prefilledDomain": "mydomain.com", "dnsRecords":[ { "type":"CNAME", "host":"www", "value":"test.com", "ttl":300 } ] } } //All the rest of possible configuration options, see https://developers.entri.com/connect/configuration for more details. ``` #### Successful response (200 status) ```JSON JSON theme={null} { // If a custom Sharing Link domain is configured (Enterprise), "link" will reflect that domain. // See: https://developers.entri.com/connect/shared-links#sharing-link-custom-domain "link": "https://app.goentri.com/share/dd339cf05f2646539e79251f8e62f0c5", "job_id": "9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de" } ``` # Connect White Label Source: https://developers.entri.com/connect/white-label Theme, colors, icons, custom properties, and custom copy for the Connect modal. ## `whiteLabel` object **Entri Connect Growth tier and above. Included on Entri Sell** The whiteLabel property object allows you to further customize the Entri Modal UI. Please note you may need an Enterprise level account for these properties to be available. For questions about this, contact your account manager. Entri is rolling out a redesigned modal. For the `whiteLabel` properties it supports, see [Connect New UI](/connect/new-ui). The same UI powers [Entri Activate](/activate/overview). | Property | Type | Required? | Default | Description | | --------------------------- | ------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | hideEntriLogo | boolean | No | false | When true, hides all mentions of "Powered by Entri" | | hideConfetti | boolean | No | false | When true, hides the confetti upon a successful domain configuration. | | hideLinkSharingConfirmation | boolean | No | false | Removes the "I have shared the link" check from the sharing link feature modal. | | logo | string | No | null | This needs to be an https URL. When inputted, it will override the configured Application logo, and use this one instead. We recommended an SVG or PNG file that is 1:1. | | logoBackgroundColor | string | No | null | When present, this will set a background color for the logo mask. Useful if your logo uses a white color and has a transparent background. | | removeLogoBorder | boolean | No | false | When true, this will remove the rounded mask/border that is applied over your logo. | | removeShareLogin | boolean | No | false | When true, this will remove the sharing link functionality across the whole application. | | hideCompanyName | boolean | No | false | Hides the company name across the flow. | | hideProgressIndicator | boolean | No | null | Hides the progress indicator at the top of the modal | | theme | object | No | null | Described below (see [theme Object](#whitelabel-theme-object)) | | icons | object | No | null | Described below (see [icons Object](#whitelabel-icons-object)) | | customProperties | object | No | null | Described below (see [customProperties Object](#whitelabel-customproperties-object)) | | customCopy | object | No | null | Described below (see [customCopy Object](#whitelabel-customcopy-object)) | To white-label the sharing link URL itself — replacing `app.goentri.com` with your own branded domain — configure the **Sharing link URL** field in your Dashboard under Account Settings → Application Settings. This is an Enterprise-tier feature and requires no code changes. See [Sharing Link Custom Domain](/connect/shared-links#sharing-link-custom-domain) for details. ### `whiteLabel.theme` Object The `whiteLabel.theme` object allows you to customize the foreground and background colors to match your app's branding. Please note you may need an Enterprise level account for these properties to be available. For questions about this, contact your account manager. **Entri Connect Theme Options** | Property | Type | Default | Description | | ----------------------------- | ------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | bg | string | N/A | Primary brand color. Please input a hex code. Applies to primary buttons, links, and input shadow **Recommended for quick theming.** | | fg | string | N/A | Text color on primary elements. Please input a hex code. Applies to text on buttons **Recommended for quick theming.** | | darkMode | string | "disabled" | Color scheme: `"disabled"` (light), `"enabled"` (dark), or `"auto"` (matches system preference). | | colors | object | null | Custom color overrides for granular control. See [Colors Reference](#whitelabel-theme-colors-object) below for complete list of available properties. | | width | string | N/A | Modal's width | | links | string | N/A | Links color to override HTML's default blue. | | internalCustomAnimation | string | null | This enables a custom animation. Please refer to your account executive for more info. | | buttons | object | null | Contains custom properties for the buttons across all screens | | buttons.border.style | string | null | Contains the `border` css styling e.g. `solid 1px #000` | | buttons.border.radius | string | null | Contains the `border-radius` css styling e.g. `5%` or `10px` | | googleFont | object | null | Allows to set a custom GoogleFont and its properties | | googleFont.family | string | null | Overrides the Modal `font-family` | | googleFont.variant | string | null | `font-family` normal text weight e.g. `400` | | googleFont.boldWeight | string | null | `font-family` bold text weight e.g. `600` | | lightBox | object | null | Contains custom properties for the backdrop/overlay | | lightBox.color | string | null | Backdrop/overlay color. Please input a transparent hsla or rgba code | | titles | object | null | Define styling properties for the titles globally and per screen | | titles.fontSize | string | null | Overrides the `font-size` for all titles | | titles.providerLogin | object | null | Define styling properties for the Login Screen | | titles.providerLogin.fontSize | object | null | Overrides the `font-size` for the Login Screen | | secondaryLinks | object | null | Overrides styling for secondary links | | secondaryLinks.fontSize | string | null | Override the `font-size` for secondary links globally | | hideCompanyLogo | boolean | false | Hides the customer's logo on the initial screen (Does not apply to the multi-domain initial screen) | ### `whiteLabel.theme.colors` Object The `colors` object provides granular control over individual UI elements. Most of `colors` properties accept standard CSS color formats (hex, rgb, rgba, hsl, hsla), except `bg`, `fg`, `buttonPrimary`, `errorShadowColorRgb`, `errorShadowColorRgb`. **Special Format Requirements:** | Property | Format | Example | Notes | | --------------------- | ------------------ | --------------- | ----------------------------------------------------------------------------------------------------------- | | `buttonPrimary` | Hexadecimal string | `"#1f77f8"` | Same format for `them.bg`, `theme.fg`. | | `shadowColorRgb` | RGB triplet string | `"0, 0, 0"` | Comma-separated RGB values without parentheses. Used for CSS custom properties that require RGB components. | | `errorShadowColorRgb` | RGB triplet string | `"247, 38, 38"` | Same format as `shadowColorRgb`. | **Colors Property Reference:** Below is a comprehensive list of where each color property is applied in the Entri Connect modal: | Property | Applied To | CSS Variable | Example Usage | | --------------------- | ----------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------- | | `modalBackground` | Main modal container | `--modal-background` | Background of the entire modal | | `modalOverlay` | Primary overlay | `--modal-overlay` | Backdrop when modal is open | | `footerBackground` | Modal footer | `--footer-background` | Bottom section of modal | | `textPrimary` | Main text content | `--text-primary` | Primary text throughout modal | | `textSecondary` | Supporting text | `--text-secondary` | Descriptions, helper text | | `textTertiary` | Tertiary text | `--text-tertiary` | Least prominent text | | `buttonPrimary` | Primary buttons | `--button-primary` | Main action buttons | | `buttonPrimaryText` | Primary button text | `--button-primary-text` | Text on primary buttons | | `buttonSecondary` | Secondary buttons | `--button-secondary` | Less prominent action buttons | | `buttonSecondaryText` | Secondary button text | `--button-secondary-text` | Text on secondary buttons | | `linkPrimary` | Primary links | `--link-primary` | Main hyperlinks | | `linkSecondary` | Secondary links | `--link-secondary` | Less prominent links | | `linkSecondaryHover` | Secondary link hover state | `--link-secondary-hover` | Hover effect on secondary links | | `linkDestructive` | Destructive action links | `--link-destructive` | Close app button | | `inputBackground` | Form input backgrounds | `--input-background` | Text input fields | | `inputBorder` | Form input borders | `--input-border` | Input field borders | | `inputText` | Input field text | `--input-text` | Text inside input fields | | `inputPlaceholder` | Input placeholder text | `--input-placeholder` | Placeholder text in inputs | | `divider` | Section dividers | `--divider` | Horizontal rules, separators | | `border` | Generic borders | `--border` | General border color | | `shadowColorRgb` | Shadow color components | `--shadow-color-rgb` | RGB values for shadow colors | | `tableHeaderBg` | Table header background | `--table-header-bg` | Header row of tables | | `tableHeaderBorder` | Table header borders | `--table-header-border` | Border around table headers | | `tableCellBorder` | Table cell borders | `--table-cell-border` | Borders between table cells | | `tableRowEvenBg` | Even table row background | `--table-row-even-bg` | Zebra striping for tables | | `tableRowHoverBg` | Table row hover background | `--table-row-hover-bg` | Row background on hover | | `errorBorder` | Error state borders | `--error-border` | Borders on error inputs | | `errorText` | Error message text | `--error-text` | Error message color | | `errorBackground` | Error state background | `--error-background` | Background for error states | | `errorShadowColorRgb` | Error shadow color components | `--error-shadow-color-rgb` | RGB values for error shadows | | `copyRecordValueText` | Manual records value text | — | DNS value text inside each copy chip on the manual setup screen. Falls back to `buttonPrimary` when unset. | **Dark Mode Preset:** When `darkMode: 'enabled'` is set, Entri automatically applies a comprehensive dark color scheme. You can still override individual colors by specifying them in the `colors` object after setting `darkMode`. **Quick Theme + Advanced Customization:** The `bg` and `fg` properties provide fast global theming, while the `colors` object allows granular control. They work together - `colors` properties override the automatic mappings from `bg`/`fg` where specified. **Example:** ```javascript theme={null} theme: { bg: "#1f77f8", // Quick: sets buttons, links, shadows fg: "#FFFFFF", // Quick: sets text on primary elements colors: { modalBackground: "#F5F5F5", buttonPrimary: "#FF0000" // Override: specific customization } } ``` **Entri Sell Theme Options** | Property | Type | Default | Description | | ---------------- | ------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | primary | string | N/A | Main color used in your theme. It’s usually the most recognizable color in the interface. Example: `"#012939"` | | onPrimary | string | N/A | Color used for text and icons that appear on top of the primary color. Example: `"#ffffff"` | | secondary | string | N/A | Color that complements the primary color and is used for secondary UI elements. Example: `"#012939"` | | onSecondary | string | N/A | Color used for text and icons that appear on top of the secondary color. Example: `"#ffffff"` | | headerBackground | string | N/A | Background color of the header. Accepts any valid CSS color code (e.g., hex, rgb). If not defined, it will default to the primary color value. Example: `"#123456"` | | interactive | string | N/A | Color for interactive elements such as radio buttons. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#ffcc00"` | | premiumBadge | string | N/A | Background color of premium badge components. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#00ffcc"` | | onPremiumBadge | string | N/A | Text color on the premium badge. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#ffffff"` | | priceBadge | string | N/A | Background color of the price badge component. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#ff6600"` | | onPriceBadge | string | N/A | Text color on the price badge. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#ffffff"` | | activating | string | N/A | Background color of the “Search Domain” button. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#ff4500"` | | onActivating | string | N/A | Text color on the “Search Domain” button. Accepts any valid CSS color code (e.g., hex, rgb). Example: `"#000000"` | ### `whiteLabel.icons` Object The `whiteLabel.icons` object allows you to customize the default icons used across the flow. | Property | Type | Default | Description | | -------------------------------------- | ------ | ------- | ---------------------------------------------------------------------------------------------- | | icons | object | null | Contains the icons' override details. These need to be set by your assigned Sales Engineer. | | icons.initialStep | object | null | Contains the Initial screen's icons override details | | icons.initialStep.secureIcon | string | null | Icon code to use as replacement for the "secure" part of the introduction section | | icons.initialStep.easyIcon | string | null | Icon code to use as replacement for the "easy" part of the introduction section | | icons.domainAnalysis | object | null | Contains the Domains analysis' icons override details | | icons.domainAnalysis.stepInitial | string | null | Icon code to use as replacement for the undone check in the Domain analysis screen | | icons.domainAnalysis.stepInactive | string | null | Icon code to use as replacement for the inactive check in the Domain analysis screen | | icons.domainAnalysis.stepFinished | string | null | Icon code to use as replacement for the done check in the Domain analysis screen | | icons.providerLogin | object | null | Contains the Initial screen's icons override details | | icons.providerLogin.user | string | null | Icon code to use as replacement for the user icon within the username input | | icons.providerLogin.password | string | null | Icon code to use as replacement for the password icon within the password input | | icons.providerLogin.passwordEyeVisible | string | null | Icon code to use as replacement for the showing the password feature within the password input | | icons.providerLogin.passwordEyeHidden | string | null | Icon code to use as replacement for the hiding the password feature within the password input | ### `whiteLabel.customProperties` Object **Enterprise tier.** The `whiteLabel.customProperties` object allows you to set different behaviours related specifically to specific elements or a screen's behavior. | Property | Type | Default | Description | | ------------------------------------------- | ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | embeddedSelector | string | null | A valid CSS selector (e.g., #entri-container) specifying the target element where the Entri Connect login UI should be embedded. If null, the authentication flow will open in a modal. | | congratulations.disableScreen | boolean | false | If set to true, disables the congratulations screen in the modal after the domain connection. | | initialScreen | object | null | Contains properties for the initial screen | | initialScreen.showManualInstructionsCTA | boolean | false | Enables the go-to manual link in the initial screen. Closes the modal and adds the `authenticateManuallyClicked: true` key to the `onEntriClose` event. | | initialScreen.disableScreen | boolean | false | Disables the the initial screen. If prefilledDomain is in use, then the user will land directly on the domain analysis screen. If no prefilledDomain is in use, then the user will land on the domain input screen. | | errorScreen | object | null | Contains properties for the error screen | | errorScreen.genericError | object | null | Contains properties for the Generic error screen specifically | | errorScreen.genericError.image | object | null | Overrides the default image/animation shown for Generic errors | | errorScreen.sessionError | object | null | Contains properties for the Session error screen specifically | | errorScreen.sessionError.image | object | null | Overrides the default image/animation shown for Session errors | | existingRecords | object | null | Contains properties for the Existing records screen | | existingRecords.disableScreen | boolean | false | Disables the Existing Records override prompt and confirms automatically | | fwdToColleague.background | Object or String | null | Changes all possible css background properties. Can be used as **String**, working as described in the `background` [CSS property definition](https://developer.mozilla.org/en-US/docs/Web/CSS/background), or as an **Object**, making use of all possible background sub-properties using camelCase syntax keys, for example: `background:{backgroundColor:"red"}` | | manualConfiguration | object | null | Contains properties for the manual screen guide | | manualConfiguration.disableScreen | boolean | false | Prevents the manual screen from being shown. Adds the `manualScreenDisabled:true` extra property to the onEntriClose event | | providerLogin | object | null | Contains properties for the provider login screen | | providerLogin.showEntriToS | boolean | false | Displays the terms of service on the Provider Login screen | | providerLogin.disableExtendedLoginAnimation | boolean | false | Disables the extended login animation and leaves only the default one in place. | | providerLogin.changeProvider | object | null | Sets visual changes to the change provider feature on the login screen | | providerLogin.changeProvider.hide | boolean | false | Hides the "Change provider" feature | | providerLogin.gotoManualLink | object | null | Contains translations for customizing the "go to our manual setup" link rendered on the Provider Login and DomainConnect/OAuth screens. | | providerLogin.gotoManualLink.disable | boolean | false | Hides the go to manual text copy | | providerLogin.gotoManualLink.\$\{locale} | string | null | Sets the **go to manual setup link** translation text for the specific locale. Wrap clickable text in `` tags. Also accepts ``, ``, `
` tags. Example: `"Follow our manual setup guide"`. | | providerLogin.forwardLink.hideLeftArrow | boolean | false | Hides the `>>` icon at the left of the records-preview link. | | providerLogin.forwardLink.style | Object | false | Allows to include custom styling for to the forward to a colleague link. Each property has to be in added in camelCase format, eg. `background: "#000"`, `color: "white"`, `padding: "0.5rem 1rem"`, `borderRadius: "5px"`, `textDecoration: "none"`, etc. | | providerLogin.forwardLink.disable | boolean | false | Disables the *Forward Link* feature. | | providerLogin.forwardLink.hide | boolean | false | Hides the *Forward Link* feature entirely, so the forward-to-a-colleague link does not render on the Provider Login screen. | | providerLogin.recordsPreview\.hide | boolean | false | Hides the records preview feature/link. | | sharedFlow\.background | Object or String | null | Changes all possible css background properties. Can be used as **String**, working as described in the `background` [CSS property definition](https://developer.mozilla.org/en-US/docs/Web/CSS/background), or as an **Object**, making use of all possible background sub-properties using camelCase syntax keys, for example: `background:{backgroundColor:"red"}` | | sharedFlow\.hideDecorations | boolean | false | Disables the default Entri background decorations on the shared flow | | general.hideCloseButton | boolean | false | When `true`, hides the close (X) button in the widget header. The button space is preserved in the layout but remains invisible, which is useful when your own UI is responsible for dismissing the widget. | | general.useCustomExitConfirmation | boolean | false | When `true`, Entri's default "Are you sure you want to leave?" exit confirmation dialog is suppressed and the [`onEntriRequestClose`](/connect/events#onentrirequestclose-event) event fires instead when the user clicks the close (X) button. The host app must then run its own confirmation UI and call [`entri.close()`](/connect/sdk#entri-close) to dismiss the widget. | | general.embeddedMode.fillContainer | boolean | false | When `true`, makes the embedded widget fill its parent container at 100% width and height, and removes the default border-radius and box-shadow. Requires `general.embeddedMode.selector` to be set. Useful when you want the widget to be fully responsive inside your own layout container. | ### `whiteLabel.customCopy` Object **Entri Connect Premium tier and above. Included on Entri Sell** The `whiteLabel.customCopy` object allows you to customize certain texts along the flow, in order to match your brand's tone or in case the default copy doesn't exactly fit your use case. The default and only required locale (translation) needed to be used within the `whiteLabel.customCopy` object is the `EN` locale. All missing translations will default to English. Please note you may need an Enterprise level account for these properties to be available. For questions about this, contact your account manager. | Property | Type | Default | Description | | -------------------------------------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | congratulations | object | null | Contains the override details for the Congratulations screen | | congratulations.title | object | null | Overrides the title on the Congratulations screen. | | congratulations.title.\$\{locale} | string | null | Sets the **Congratulations title's** translation text for the specific locale. Accepts the `{DOMAIN}` replacement token. Accepts applying bold when using the following tags ``. | | congratulations.description | object | null | Overrides the description on the congratulations screen. | | congratulations.description.\$\{locale} | string | null | Sets the **Congratulations description's** translation text for the specific locale. Accepts applying bold when using the following tags ``. Accepts a `{USER}` (described below) and `{DOMAIN}` placeholders. | | congratulations.user | string | null | The user value to replace on the `{USER}` token in the `congratulations.description` | | congratulations.sell.title | object | null | Overrides the title on the Congratulations screen. | | congratulations.sell.title.\$\{locale} | string | null | Sets the **Congratulations title's** translation text for the specific locale. Accepts the `{DOMAIN}` and `{APPLICATION_NAME}` replacement tokens. Accepts applying bold when using the following tags ``. | | congratulations.sell.description | object | null | Overrides the description on Entri Sell congratulations screen. | | congratulations.sell.description.\$\{locale} | string | null | Sets the **Congratulations description's** translation text for the specific locale. Accepts applying bold when using the following tags ``. Accepts a `{USER}` (described below) and `{APPLICATION_NAME}` replacement tokens. | | congratulations.sell.user | string | null | The user value to replace on the `{USER}` token in the `congratulations.sell.description` | | congratulations.button | string | null | Overrides the button's text in the Congratulations screen. | | congratulations.button.\$\{locale} | string | null | Sets the **Congratulations button call to action's** translation text for the specific locale. Accepts applying bold when using the following tags ``. | | domainAnalysis | object | null | Contains the override details for the Domain analysis screen | | domainAnalysis.title | object | null | Overrides the title on the Domain analysis screen. | | domainAnalysis.title.\$\{locale} | string | null | Sets the **Domain Analysis title's** translation text for the specific locale. | | domainAnalysis.analyzing | object | null | Overrides the **analysis in progress** text on the Domain analysis screen. | | domainAnalysis.analyzing.\$\{locale} | string | null | Sets the **analysis in progress** translation text for the specific locale. | | domainAnalysis.analyzed | object | null | Overrides the **analysis done** text on the Domain analysis screen. | | domainAnalysis.analyzed.\$\{locale} | string | null | Sets the **analysis done** translation text for the specific locale. | | domainAnalysis.detecting | object | null | Overrides the **detection in progress** text on the Domain analysis screen. | | domainAnalysis.detecting.\$\{locale} | string | null | Sets the **detection in progress** translation text for the specific locale. | | domainAnalysis.detected | object | null | Overrides the **detection done** text on the Domain analysis screen. | | domainAnalysis.detected.\$\{locale} | string | null | Sets the **detection done** translation text for the specific locale. | | domainAnalysis.gettingSetupReady | object | null | Overrides the **getting setup ready** text on the Domain analysis screen. | | domainAnalysis.gettingSetupReady.\$\{locale} | string | null | Sets the **getting setup ready** in progress translation text for the specific locale. | | domainAnalysis.setupIsReady | object | null | Overrides the **setup is ready** text on the Domain analysis screen. | | domainAnalysis.setupIsReady.\$\{locale} | string | null | Sets the **setup is ready** translation text for the specific locale. | | existingRecords | object | null | Contains the override details for the Override records confirmation screen. | | existingRecords.title | object | null | Contains the translations for Overriding the title on the Override records confirmation screen. | | existingRecords.title\$.\{locale} | string | null | Sets the **Override-confirmation screen title's** translation text for the specific locale. Accepts the `{DOMAIN}` replacement token. Accepts applying bold when using the following tags ``. | | initialScreen | object | null | Contains the override details for the Initial screen | | initialScreen.title | object | null | Contains the translations for overriding the title on the initial screen. | | initialScreen.title.\$\{locale} | string | null | Sets the **title's** translation text for the specific locale. (one of the available codes such as en,es,fr, etc., for example initialScreen.title.en). Accepts applying bold when using the following tags ``. For Entri Sell, sets the title text that appears on the initial screen of the domain purchase flow, localized by language. Example: "Search for your domain" | | initialScreen.marketingCopy.\$\{locale} | string | null | Marketing copy that appears below the domain input field, localized by language. Example: *"Your website will look even better with a custom domain!"* (Entri Sell only) | | initialScreen.subTitle | object | null | Contains the translations for overriding the subtitle on the initial screen | | initialScreen.subTitle.\$\{locale} | string | null | Sets the **subtitle's** translation text for the specific locale. | | initialScreen.secure | object | null | Overrides the texts in the "secure" section on the initial screen | | initialScreen.secure.title | object | null | Contains the translations for overriding the "secure" title section on the initial screen. | | initialScreen.secure.title.\$\{locale} | string | null | Sets the **secure title's** translation text for the specific locale. | | initialScreen.secure.description | object | null | Contains the translations for overriding the "secure" description text section on the initial screen | | initialScreen.secure.description.\$\{locale} | string | null | Sets the **secure description's** translation text for the specific locale. | | initialScreen.easy | object | null | Overrides the texts in the "easy" section on the initial screen | | initialScreen.easy.title | object | null | Contains the translations for overriding the "easy" title section on the initial screen | | initialScreen.easy.title.\$\{locale} | string | null | Sets the **easy title's** translation text for the specific locale. | | initialScreen.easy.description | object | null | Contains the translations for overriding the "easy" description section on the initial screen | | initialScreen.easy.description.\$\{locale} | string | null | Sets the **easy description's** translation text for the specific locale. | | login2FA | object | null | Contains the override details for the login 2FA screen | | login2FA.description | object | null | Overrides the description on the login 2FA screen. | | login2FA.description.generic | object | null | Overrides the description on the login 2FA screen with a generic text applied to all types of 2FA. | | login2FA.description.generic.\$\{locale} | string | null | Sets the **Login 2FA description's** translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). | | login2FA.description.sms | object | null | Overrides the description on the login 2FA for SMS 2fa auth | | login2FA.description.sms.\$\{locale} | string | null | Sets the **Login 2FA description's** for SMS 2fa auth translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). | | login2FA.description.email | object | null | Overrides the description on the login 2FA for EMAIL 2fa auth | | login2FA.description.email.\$\{locale} | string | null | Sets the **Login 2FA description's** for EMAIL 2fa auth translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). | | login2FA.description.authenticatorApp | object | null | Overrides the description on the login 2FA for AUTHENTICATOR 2fa auth | | login2FA.description.authenticatorApp.\$\{locale} | string | null | Sets the **Login 2FA description's** for AUTHENTICATOR 2fa auth translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). | | manuallyScreen | object | null | Contains the step-by-step-guide override details. | | manuallyScreen.disableManualSetupDocumentationLink | boolean | false | Disables the opening of the documentation URL provided in the manualSetupDocumentation configuration property. Commonly used in combination with in combination with the onEntriManualSetupDocumentationClick event to trigger a custom js function. | | manuallyScreen.stepByStepGuide | object | null | Overrides the step-by-step-guide line of text on the manual configuration screen | | manuallyScreen.stepByStepGuide.\$\{locale} | string | null | Sets the **step-by-step-guide** translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). This field also supports links for triggering javascript functions only. These are helpful for triggering chatbots or other actions. You only need to enclose the call to action text inside the `` tags to create an "empty" link, and then catch the onEntriManualSetupDocumentationClick event. | | providerLogin | object | null | Contains the override details for the Provider Login screen | | providerLogin.message | object | null | Contains the translations for overriding the introductory message on the provider login screen. | | providerLogin.message.\$\{locale} | string | null | Sets the **Provider Login's introduction's** translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). Accepts the `{PROVIDER}` replacement token. Accepts applying bold when using the following tags ``. | | providerLogin.forwardLogin | object | null | Contains translations for customizing the **Forward login** call to action.. | | providerLogin.forwardLogin.\$\{locale} | object | null | Sets the **Forward login** translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example providerLogin.forwardLogin.en). | | providerLogin.showPreview | object | null | Contains translations for customizing the text that shows the record previews. | | providerLogin.showPreview.\$\{locale} | object | null | Sets the **Show preview** translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). | | providerLogin.hidePreview | object | null | Contains translations for customizing the text that hides the record previews. | | providerLogin.hidePreview.\$\{locale} | object | null | Sets the **Hide preview** translation text for the specific locale (one of the available codes, such as en,es,fr, etc., for example initialScreen.title.en). | Locales supported are one of form `en`, `es`, `fr`, etc. If no matching translation is found for the locale in use, then the Entri's default text will be used. Please find the full list of supported locales in [property's documentation](/connect/configuration#entrishowentriconfig). ### White label code example: ```JavaScript JavaScript theme={null} { "whiteLabel": { "hideEntriLogo": true, "hideConfetti": true, "logo": "LOGO_URL", "theme": { "fg": "#fff", "bg": "#fa7268" }, "logoBackgroundColor": "#444444", "removeLogoBorder": true, "customCopy": { "initialScreen": { "title": { "en": "Custom title", "es": "Título custom" }, "subTitle": { "en": "Custom subtitle", "es": "Subtítulo custom" } }, "manuallyScreen": { "disableManualSetupDocumentationLink": true, "stepByStepGuide": { "en": "Follow our step-by-step guide", "es": "Sigue nuestra guía paso-a-paso" } } } } } ``` # DNS Concepts Source: https://developers.entri.com/dns-concepts Essential DNS knowledge for working with Entri Understanding DNS basics helps you configure domains correctly and troubleshoot issues faster. This guide covers the concepts most relevant to Entri integrations. ## What Entri Does (and Doesn't Do) Entri is a platform that helps developers offer seamless domain experiences to their users. | Product | What it does | | ----------- | -------------------------------------------------------------------------------- | | **Connect** | Automates DNS configuration at the user's existing provider | | **Sell** | Enables in-app domain purchasing through partner registrars (IONOS, Squarespace) | | **Secure** | Provisions and manages SSL certificates | | **Power** | Powers custom domains for SaaS applications | | **Monitor** | Tracks DNS changes and alerts on issues | **Key distinction**: When users connect an existing domain through Entri Connect, we configure DNS records at their current provider. We don't transfer domains or change where they're registered. ### The Domain Ecosystem | Service | Role | Examples | | -------------------- | ---------------------------------------------- | ------------------------- | | **Domain Registrar** | Sells and registers domain names | GoDaddy, Namecheap, IONOS | | **DNS Provider** | Hosts DNS records (often the registrar itself) | Cloudflare, Route 53 | | **Entri** | Automates the configuration process | — | ## DNS Record Types ### A Record Points a domain to an IPv4 address. ``` example.com → 93.184.216.34 ``` **Use case**: Pointing your root domain to a server. ### AAAA Record Points a domain to an IPv6 address. ``` example.com → 2606:2800:220:1:248:1893:25c8:1946 ``` **Use case**: Same as A record, but for IPv6. ### CNAME Record Points a domain to another domain (an alias). ``` www.example.com → example.com shop.example.com → myapp.herokuapp.com ``` **Use case**: Subdomains that should resolve to another hostname. **CNAME records cannot be used at the root domain (@)** on most DNS providers. See [CNAME at Root](#cname-at-root-the-technical-limitation) below. ### MX Record Directs email to mail servers. ``` example.com → mail.example.com (priority: 10) ``` **Use case**: Email delivery. Always requires a priority value. ### TXT Record Stores text data, commonly used for verification and email authentication. ``` example.com → "v=spf1 include:_spf.google.com ~all" ``` **Use case**: SPF, DKIM, DMARC, domain verification. ### NS Record Specifies which DNS servers are authoritative for the domain. ``` example.com → ns1.dnsprovider.com ``` **Use case**: Delegating DNS to a different provider. Rarely needed in typical Entri flows. ## Root Domain vs Subdomain | Term | Example | Also called | | --------------- | ------------------------------------- | ---------------------------- | | **Root domain** | `example.com` | Apex domain, naked domain, @ | | **Subdomain** | `www.example.com`, `shop.example.com` | — | In DNS configuration: * `@` or blank host = root domain * Any other value = subdomain ## CNAME at Root: The Technical Limitation ### Why it doesn't work The DNS specification (RFC 1034) states that if a CNAME record exists for a name, no other records can exist for that name. Since root domains typically need MX records (for email) and often TXT records (for verification), a CNAME at root would break email and other services. ### What happens when you try | Provider | Behavior | | -------------- | --------------------------------------------- | | Most providers | Reject the record or show an error | | Cloudflare | Accepts it via "CNAME flattening" (see below) | | Route 53 | Accepts it via "ALIAS" records | ### Solutions **Option 1: Use A records instead** ```javascript theme={null} dnsRecords: [ { type: "A", host: "@", value: "93.184.216.34", ttl: 300 } ] ``` **Option 2: Use a provider that supports CNAME flattening** Some DNS providers work around this limitation using techniques like CNAME flattening, ALIAS records, or ANAME records. The provider resolves the CNAME to its final IP address and returns that as an A record. The user sees a CNAME in their DNS panel, but queries receive an A record response. Support varies by provider and may have restrictions (for example, some only support aliasing to their own resources). Use the `checkDomain` response to detect whether the user's provider supports this: ```javascript theme={null} const result = await entri.checkDomain("example.com", config); if (result.cnameFlattening) { // Provider supports CNAME at root — safe to use CNAME records } ``` **Option 3: Use Entri Secure** [Entri Secure](/secure/overview) handles root domain configuration automatically by pointing A records to Entri's servers, which then proxy traffic to your application. ```javascript theme={null} { secureRootDomain: true, wwwRedirect: true, // ... } ``` ### Detecting flattening support The `checkDomain` response includes CNAME flattening information: ```javascript theme={null} const result = await entri.checkDomain("example.com", config); if (result.cnameFlattening) { // Provider supports CNAME at root } ``` ## TTL (Time to Live) TTL specifies how long DNS resolvers should cache a record before checking for updates. | TTL | Meaning | Use case | | ---------------- | ------------------- | --------------------------------------- | | 300 (5 min) | Minimum recommended | During setup, when changes are expected | | 3600 (1 hour) | Standard | Normal operation | | 86400 (24 hours) | Long cache | Stable records that rarely change | Entri uses 300 seconds as the minimum TTL. Some providers enforce their own minimums and will override lower values. ## DNS Propagation After DNS changes, it takes time for the new records to be visible worldwide. This is called propagation. **Typical propagation times:** * Same DNS provider: Minutes * Across the internet: 15 minutes to 48 hours **What affects propagation:** * Previous TTL value (old records stay cached until TTL expires) * Geographic location of DNS resolvers * Provider-specific delays **Checking propagation:** Entri automatically monitors propagation and sends webhooks when records are fully propagated. You can also use external tools like [whatsmydns.net](https://www.whatsmydns.net). ## Common Misconceptions ### "Entri transferred my domain" Entri Connect configures DNS records at your existing provider. It doesn't transfer domains between registrars. If you purchased a domain through Entri Sell, that domain is registered with a partner registrar (like IONOS) and managed through their systems. ### "I need to change my nameservers to use Entri" Not required for most use cases. Entri works with your existing DNS provider. Nameserver changes are only needed if you're delegating DNS to a different provider entirely. ### "My DNS changes should be instant" DNS propagation takes time. Even after Entri reports success, some users may not see changes immediately due to caching. ### "CNAME and A records do the same thing" They're fundamentally different. A records point to IP addresses; CNAME records point to other domain names. This matters for root domains and for services that change IPs frequently. ## Related Pages * [Provider Limitations](/provider-limitations) - Specific constraints by DNS provider * [Errors Overview](/errors/overview) - Common errors and how to resolve them * [Handling DKIM, SPF, DMARC](/handling-dkim-spf-dmarc-records) - Email authentication records # Overview Source: https://developers.entri.com/entri-mcp Enable AI agents to search, register, and connect domains through Entri's MCP server. ## Overview Model Context Protocol (MCP) is an open standard that gives AI agents a consistent, secure way to discover and use external tools and services. The **Entri MCP server** exposes Entri Connect and Sell products as MCP tools, allowing AI assistants to handle domain operations on behalf of your users entirely within the conversation. **MCP Server URL:** `https://mcp.entri.com/mcp` Depending on which Entri product and registrar are configured for your account, the MCP tools behave differently: * **Entri Connect** — Returns a sharing link the user follows to connect an existing domain to your platform. * **Entri Sell (IONOS / Squarespace)** — Returns a sharing link the user follows to purchase a domain through the registrar's checkout. * **Entri Sell Enterprise (direct registration)** — Registers domains directly on the user's behalf, with optional purchase authorization and Lightning Mode for zero-friction flows. *** ## Authentication Entri MCP uses **OAuth 2.0 machine-to-machine (M2M) authentication**, following the [MCP Authorization specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) and the **client credentials grant** defined in [RFC 6749 §4.4](https://www.rfc-editor.org/rfc/rfc6749#section-4.4). Your **Client ID** and **Client Secret** (available in the Dashboard) are used to obtain a short-lived access token from the OAuth Authentication URL. That token is then passed as a Bearer token on each MCP request. **Token request:** ```http theme={null} POST https://mcp.entri.com/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials &client_id=YOUR_CLIENT_ID &client_secret=YOUR_CLIENT_SECRET &userId=END_USER_ID &geo=us ``` | Parameter | Required | Description | | --------------- | -------- | ------------------------------------------------------ | | `grant_type` | Yes | Must be `client_credentials` | | `client_id` | Yes | Your Client ID from the Dashboard | | `client_secret` | Yes | Your Client Secret from the Dashboard | | `userId` | No | ID of the end user on whose behalf the request is made | | `geo` | No | Geographic region for the request (e.g. `us`, `fr`) | **Token response:** ```json theme={null} { "access_token": "eyJ...", "token_type": "Bearer" } ``` Use the returned `access_token` as a Bearer token on all MCP requests: ``` Authorization: Bearer {access_token} ``` MCP hosts using `mcp-remote` (VS Code, Cursor, etc.) can pass credentials directly as `Authorization: Bearer YOUR_CLIENT_ID:YOUR_CLIENT_SECRET` in the configuration JSON — no token exchange needed. For programmatic or server-side integrations, use the OAuth flow above. *** ## Dashboard — MCP Settings All MCP configuration is managed under **Dashboard → Application Settings → MCP Settings**. ### MCP Connection Credentials These credentials are used to authenticate your MCP server with Entri. Find them under the **MCP Connection** section of MCP Settings. | Field | Description | | ---------------------------- | ---------------------------------------------------------------------------------- | | **Client ID** | Your application identifier. Read-only, copyable. | | **Client Secret** | Your secret key. Masked by default. Rotatable from Application Settings. | | **OAuth Authentication URL** | Token endpoint for obtaining short-lived access tokens (client credentials grant). | | **MCP Server URL** | The remote MCP server endpoint to configure in your MCP host. | *** ### Entri Connect Settings Configure how the MCP retrieves DNS records and provider options for Connect domain flows. The `connect-domain` tool always returns a **sharing link** — this setting determines what configuration is pre-loaded when the user opens that link. #### Configuration Source Use Static Configuration when every user needs the same DNS records. Provide a single Entri configuration that will be applied to all Connect flows. ```json theme={null} { "whiteLabel":{ ... }, "dnsRecords": [ { "type": "A", "host": "@", "value": "93.184.216.34", "ttl": 300 } ], //... every other optional key } ``` Use Dynamic Configuration when each user needs a unique set of DNS records. Provide an endpoint URL that Entri will call per-request to retrieve the full configuration. Entri authenticates requests to your endpoint using **Basic Authentication**. Provide the username and password in the fields below the URL. **Endpoint requirements:** * Must accept `GET` requests. * Must support Basic Authentication. * Entri will include `userId` as a query string parameter (e.g. `?userId=abc123`) so you can return a user-specific configuration. * Must return an Entri configuration object. Minimal example: ```json theme={null} { "applicationId": "your-application-id", "dnsRecords": [ { "host": "@", "type": "TXT", "ttl": 300, "value": "this-is-a-sample-value" } ] } ``` For the full configuration reference, see the [API Reference](/connect/configuration). *** ### Entri Sell Settings Configure how the MCP retrieves DNS records and whitelabel options for Sell domain purchase flows. This applies when your account uses a **sharing registrar** (IONOS or Squarespace) — the purchase flow is presented to the user as a link. #### Configuration Source Use Static Configuration when every user needs the same DNS records and branding. Provide a single Entri configuration applied to all Sell flows. ```json theme={null} { "whiteLabel":{ ... }, "dnsRecords": [ { "type": "A", "host": "@", "value": "93.184.216.34", "ttl": 300 } ], //... every other optional key } ``` Use Dynamic Configuration when each order requires a unique set of DNS records or custom branding. Provide an endpoint URL that Entri will call per-request. Entri authenticates requests to your endpoint using **Basic Authentication**. Provide the username and password in the fields below the URL. **Endpoint requirements:** * Must accept `GET` requests. * Must support Basic Authentication. * Entri will include `userId` as a query string parameter (e.g. `?userId=abc123`) so you can return a user-specific configuration. * Must return an Entri configuration object. Minimal example: ```json theme={null} { "applicationId": "your-application-id", "dnsRecords": [ { "host": "@", "type": "TXT", "ttl": 300, "value": "this-is-a-sample-value" } ] } ``` For the full configuration reference, see the [API Reference](/connect/configuration). *** ### Entri Sell Enterprise Settings These settings are available on Entri Sell Enterprise. #### Purchase Authorization Enable this toggle to require authorization before each domain purchase is processed. When enabled, Entri calls your endpoint and expects an approval decision before registering the domain. When disabled, purchases proceed automatically. Entri authenticates requests to your authorization endpoint using **Basic Authentication**. Provide the endpoint URL, username, and password in the Purchase Authorization section. See [Purchase Authorization](#purchase-authorization) for the full endpoint contract. *** ## How Each Product Works via MCP The behavior of `create-domain-order` and `connect-domain` depends on which Entri product and registrar are configured for your account. The registrar is resolved automatically from your application settings in this order: 1. If `preferred_registrar` is `"ionos"` or `"squarespace"` → sharing registrar flow 2. Else if direct registration is enabled → Sell Enterprise flow 3. Otherwise → no registrar configured; domain tools will return an error ### Entri Connect The `connect-domain` tool generates a **sharing link** that the user must open to complete DNS configuration. Entri's guided flow walks the user through logging in to their DNS provider and applying the required records. * The AI agent must present the link to the user and ask them to open it. * DNS propagation status is tracked with `check-connection-status` using the returned `jobId`. * The DNS records and provider options loaded in the flow are sourced from your **Entri Connect Settings** (Static or Dynamic). ### Entri Sell — IONOS / Squarespace When your account uses IONOS or Squarespace as the registrar, `create-domain-order` returns a **sharing link** instead of registering the domain directly. The user must follow the link to complete the purchase through the registrar's checkout. * The AI agent must present the link and ask the user to complete the purchase. * Order progress is tracked with `check-order-status` using the returned `jobId`. * The DNS records and whitelabel configuration for the purchase flow come from your **Entri Sell Settings** (Static or Dynamic). ### Entri Sell Enterprise With Entri Sell Enterprise, `create-domain-order` **registers the domain directly** — no redirect or external checkout required. * If **Purchase Authorization** is enabled, Entri calls your authorization endpoint before registering. The domain is only registered if your endpoint returns `200`. See [Purchase Authorization](#purchase-authorization). * If **Lightning Mode** is enabled, contact details are sourced automatically from your Sell Settings. The AI agent does not need to collect them from the user. See [Lightning Mode](#lightning-mode). * Order progress is tracked with `check-order-status` using the returned `orderId`. **Summary:** | Product / Registrar | `create-domain-order` outcome | User action required? | | -------------------------- | ------------------------------------- | ------------------------------------------- | | Sell — IONOS / Squarespace | Returns a sharing link + `jobId` | Yes — user completes checkout via the link | | Sell Enterprise | Registers domain directly + `orderId` | No (unless authorization is rejected) | | Connect (`connect-domain`) | Returns a DNS config link + `jobId` | Yes — user completes DNS setup via the link | *** ## Purchase Authorization Purchase Authorization is available on Entri Sell Enterprise. Purchase Authorization lets you enforce business rules before a domain is registered — entirely on the backend, invisible to the end user. When the toggle is enabled, Entri calls your endpoint after the AI agent requests a registration, and proceeds only if you approve. **Common use cases:** * **Balance validation** — Check if the user has sufficient credits or funds. * **Spending limits** — Enforce per-user domain quotas or monthly purchase caps. * **Fraud prevention** — Block suspicious registration patterns before they complete. * **Manual approval** — Gate expensive or sensitive domains behind admin review. ### How It Works 1. AI agent calls `create-domain-order(domain: "example.com")`. 2. Entri calls `POST {your_authorization_url}` using the Basic Auth credentials configured in the Dashboard. 3. Your endpoint validates the request and responds with an HTTP status code. 4. Entri acts on the response: * `200` → Domain registration proceeds. * `401` → Registration is rejected; the AI agent receives an error. * Any other code → Treated as an error; registration is blocked and an email alert is sent to your account. ### Endpoint Response Contract ```json Approved (HTTP 200) theme={null} { "dnsRecords": [ { "type": "A", "host": "@", "value": "93.184.216.34", "ttl": 300 }, { "type": "CNAME", "host": "www", "value": "example.com", "ttl": 300 } ] } ``` ```http Rejected (HTTP 401) theme={null} (no response body) ``` The `dnsRecords` array in the `200` response is applied to the domain upon successful registration. This lets you return user-specific DNS records dynamically at authorization time, rather than relying on a static configuration. If your authorization endpoint is unreachable, times out, or returns any code other than `200` or `401`, the registration is blocked and an email alert is sent to your account. Make sure your endpoint is highly available when this toggle is enabled. *** ## Lightning Mode Lightning Mode is available on Entri Sell Enterprise. Lightning Mode lets you pre-configure a single set of contact details that Entri applies automatically to every domain registration. When enabled, the AI agent no longer needs to collect contact information from the user — domains can be registered with a single conversational prompt. **The goal is zero-friction domain registration.** Instead of the AI agent asking for name, address, phone, and email before registering, the user just says "register example.com" and it's done. **When Lightning Mode is ON:** * The `create-domain-order` tool uses the contact details saved in **Dashboard → Application Settings → Sell Settings** for all contact types (Owner, Technical, and Registrant). * ICANN email notifications — including verification emails — are **not sent**. * Any `contactDetails` passed by the AI agent are ignored. **When Lightning Mode is OFF:** * The AI agent will ask the end user for their contact details directly in the chat before completing the registration. * ICANN emails are sent normally. ### Configure Lightning Mode 1. Navigate to **Dashboard → Application Settings → Sell Settings**. 2. Enable the **Lightning Mode** toggle. 3. Fill in the default contact information: * First Name, Last Name * Email address * Phone number with country code (e.g., `+15551234567`) * Address, City, State, ZIP code, Country 4. Click **Save Contact**. The contact information saved here is applied uniformly to all contact types — Owner, Technical, and Registrant. You can update these details at any time, and all subsequent registrations will use the updated values. *** ## MCP Tools The Entri MCP server exposes 6 tools across the Connect and Sell products. For the full reference — including parameters, response shapes, and status values — see the [MCP Tool Reference](/entri-mcp-tools). # Tool Reference Source: https://developers.entri.com/entri-mcp-tools Reference for all tools exposed by the Entri MCP server. The Entri MCP server exposes the following tools. For feedback or to request additional tools, contact [support@entri.com](mailto:support@entri.com). | Tool | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `search-domain-availability` | Check if a domain is available for registration. Returns availability, price, and renewal price. | | `generate-domain-suggestions` | Generate AI-powered domain name suggestions from keywords or a business description. | | `create-domain-order` | Register a domain or initiate a purchase flow. Behavior depends on the registrar configured for your account. | | `connect-domain` | Generate a guided DNS configuration link for the user to connect an existing domain to your platform. | | `check-connection-status` | Check DNS propagation and connection status using the `jobId` returned by `connect-domain`. | | `check-order-status` | Check the current status of a domain registration order using the `orderId` or `jobId` from `create-domain-order`. | *** ### Tool: search-domain-availability Checks whether a domain is available for registration, along with its registration and renewal pricing. **Example:** ```txt theme={null} User: "Is startup.ai available?" AI calls: search-domain-availability(domain: "startup.ai") ``` **Response (available):** ```json theme={null} { "domain": "startup.ai", "available": true, "price": "$49.99/year", "renewalPrice": "$89.99/year", "message": "startup.ai is available!" } ``` **Response (unavailable):** ```json theme={null} { "domain": "startup.ai", "available": false, "message": "startup.ai is taken." } ``` *** ### Tool: generate-domain-suggestions Generates AI-powered domain name suggestions based on keywords or a business description. Returns only available domains, sorted by relevance. Always returns a minimum of 5 suggestions. The top recommendation is marked with 🏆. **Example:** ```txt theme={null} User: "Suggest domains for my AI coding assistant startup" AI calls: generate-domain-suggestions(keywords: "AI coding assistant startup", limit: 5) ``` **Response:** ```json theme={null} { "keywords": "AI coding assistant startup", "suggestions": [ { "domain": "🏆 codecopilot.shop", "price": "$12.99/year", "renewalPrice": "$24.99/year" }, { "domain": "codecopilot.ai", "price": "$49.99/year", "renewalPrice": "$89.99/year" }, { "domain": "aicode.dev", "price": "$29.99/year", "renewalPrice": "$39.99/year" } ], "count": 3, "message": "Display these domain suggestions in a table with columns: Domain, Price, Renewal Price. The first domain (marked with 🏆) is the recommended option." } ``` *** ### Tool: create-domain-order Registers a domain or initiates a purchase flow. The response shape depends on which registrar is configured for your account — see [How Each Product Works via MCP](/entri-mcp#how-each-product-works-via-mcp). **Example:** ```txt theme={null} User: "Register startup.ai for me" AI calls: create-domain-order(domain: "startup.ai") ``` **Response — Sell Enterprise (direct registration):** ```json theme={null} { "success": true, "domain": "startup.ai", "orderId": "ORD-1707000000-abc123", "status": "pending", "message": "Order created for startup.ai. Use check-order-status with orderId \"ORD-1707000000-abc123\" to track registration progress." } ``` **Response — Sharing registrar (IONOS / Squarespace):** ```json theme={null} { "success": true, "domain": "startup.ai", "link": "https://connect.entri.com/sell-flow?token=abc123...", "jobId": "JOB-1707000000-abc123", "message": "Sharing purchase link for startup.ai — Present this link to the user and ask them to complete the purchase. Then use check-order-status with jobId \"JOB-1707000000-abc123\" to track progress." } ``` **Response — Error:** ```json theme={null} { "success": false, "domain": "startup.ai", "error": "HTTP 422", "message": "Contact details are required for this registration." } ``` **Important notes:** * For sharing registrars, the AI agent **must present the link** to the user and ask them to complete the purchase. * For Sell Enterprise, contact details are sourced automatically when Lightning Mode is on. Do not ask the user for contact details upfront — always attempt registration with just the `domain` first. * `dnsRecords` is not a parameter of this tool. DNS records are configured via your MCP Settings (Static or Dynamic) or returned by your Purchase Authorization endpoint. * After a successful order, always poll `check-order-status` with the returned `orderId` or `jobId` until registration is complete. | Registrar / Plan | Response | Next step | | ------------------- | ---------------------------- | ----------------------------------------------------------------- | | Sell Enterprise | Returns `orderId` + `status` | Poll `check-order-status` with `orderId` | | IONOS / Squarespace | Returns `link` + `jobId` | Present link to user, then poll `check-order-status` with `jobId` | *** ### Tool: connect-domain Generates a guided DNS configuration link for an existing domain. The user must open this link to complete the DNS setup through Entri's connection flow. **Example:** ```txt theme={null} User: "Connect startup.ai to my project" AI calls: connect-domain(domain: "startup.ai") ``` **Response (success):** ```json theme={null} { "success": true, "domain": "startup.ai", "link": "https://connect.entri.com/flow?token=abc123...", "jobId": "JOB-1707000000-abc123", "message": "Connect flow link for startup.ai: https://connect.entri.com/flow?token=abc123..." } ``` **Response (error):** ```json theme={null} { "success": false, "domain": "startup.ai", "error": "HTTP 404", "message": "The domain startup.ai could not be found in your account." } ``` **Important notes:** * The AI agent **must present the returned `link`** to the user and ask them to open it. The DNS setup is completed by the user in Entri's flow — the agent cannot complete it on their behalf. * After the user opens the link, use `check-connection-status` with the returned `jobId` to monitor DNS propagation. * DNS propagation can take up to 48 hours in rare cases. *** ### Tool: check-connection-status Retrieves the current DNS propagation and connection status for a domain. Use it after `connect-domain` to verify whether DNS records have fully propagated. **Example:** ```txt theme={null} User: "Is startup.ai fully connected yet?" AI calls: check-connection-status(jobId: "JOB-1707000000-abc123") ``` **Response (connected):** ```json theme={null} { "jobId": "JOB-1707000000-abc123", "connected": true, "status": "completed", "domain": "startup.ai", "message": "DNS configuration complete! Domain startup.ai is now connected." } ``` **Response (in progress):** ```json theme={null} { "jobId": "JOB-1707000000-abc123", "connected": false, "status": "pending", "domain": "startup.ai", "message": "Connection JOB-1707000000-abc123 is still in progress (status: pending). Check again shortly." } ``` **Response (failed):** ```json theme={null} { "jobId": "JOB-1707000000-abc123", "connected": false, "status": "failed", "message": "Connection JOB-1707000000-abc123 has failed with status: failed." } ``` **Status values:** | Status | Meaning | | ----------- | ----------------------------------- | | `pending` | User hasn't completed the flow yet | | `completed` | DNS fully propagated and connected | | `failed` | Connection failed | | `error` | Unexpected error | | `expired` | The connect link expired before use | *** ### Tool: check-order-status Retrieves the current processing status of a domain registration order. Use it after `create-domain-order` to track progress. **Example:** ```txt theme={null} User: "What's the status of my domain registration?" AI calls: check-order-status(orderId: "ORD-1707000000-abc123") ``` **Response — Sell Enterprise (registered):** ```json theme={null} { "orderId": "ORD-1707000000-abc123", "status": "registered", "message": "Domain registration complete! Order ORD-1707000000-abc123 is now registered." } ``` **Response — Sharing registrar (completed):** ```json theme={null} { "orderId": "JOB-1707000000-abc123", "status": "completed", "domains": [ { "domain": "startup.ai", "status": "registered" } ], "message": "Order JOB-1707000000-abc123 is complete." } ``` **Response (in progress):** ```json theme={null} { "orderId": "ORD-1707000000-abc123", "status": "pending", "message": "Order ORD-1707000000-abc123 is still in progress (status: pending). Check again shortly." } ``` **Response (failed):** ```json theme={null} { "orderId": "ORD-1707000000-abc123", "status": "failed", "message": "Order ORD-1707000000-abc123 has failed with status: failed." } ``` **Status values:** | Status | Meaning | | -------------------------- | ---------------------------------------------------------- | | `pending` | Registration queued, not yet processed | | `registration_in_progress` | Actively being processed (Sell Enterprise) | | `in_progress` | Actively being processed (IONOS / Squarespace) | | `registered` | Domain successfully registered (Sell Enterprise) | | `completed` | Purchase completed (IONOS / Squarespace) | | `failed` | Registration failed | | `cancelled` | Order was cancelled | | `rejected` | Registration was rejected | | `error` | Unexpected error occurred | | `expired` | Sharing link expired before use (IONOS / Squarespace only) | # Errors Overview Source: https://developers.entri.com/errors/overview Common errors and how to resolve them This page covers the most common errors when integrating Entri. For the complete API documentation, see the [API Reference](/connect/configuration). ## Initialization Errors These errors occur when calling `showEntri()` or `purchaseDomain()` and prevent the modal from opening. The user sees "Entri is misconfigured. Please contact support for assistance." Check browser console for the actual error. ### showEntri() Errors | Error | Console Message | Cause | Fix | | --------------------- | --------------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------ | | Missing applicationId | "Missing parameter: applicationId" | Config object missing `applicationId` | Add your applicationId from the dashboard | | Invalid domain | "This domain is invalid or uses an unsupported TLD (unlisted )" | Domain doesn't pass validation | Check domain format (examples of invalid: `asdfasdf.asdfasdf`, `domain.`, `123.456`) | | Configuration error | "Configuration error, please refer to the docs..." | `forceManualSetup: true` without `prefilledDomain` | Add `prefilledDomain` or remove `forceManualSetup` | ### purchaseDomain() Errors | Error | Console Message | Cause | Fix | | ------------------------ | ------------------------------------------ | ----------------------------------------------------------- | -------------------------------------------- | | Failed to fetch settings | "Failed to fetch application settings" | API call failed (network error, invalid appId, or API down) | Check network, verify appId exists | | Missing registrar | "Your preferred registrar is missing..." | No `preferred_registrar` in config or app settings | Contact support to configure registrar | | Unsupported registrar | "Preferred registrar is not supported..." | Invalid `preferred_registrar` value | Use supported values: `ionos`, `squarespace` | ## Modal Error Codes These errors appear in the `error` object within `onEntriClose` and `onEntriStepChange` events when the user exits after seeing an error screen. ```javascript theme={null} window.addEventListener('onEntriClose', (event) => { if (event.detail.error) { console.log('Error code:', event.detail.error.code); console.log('Title:', event.detail.error.title); console.log('Details:', event.detail.error.details); } }); ``` | Code | Title | Description | | --------------------------- | -------------------------------- | ------------------------------------------------------------- | | AccessDeniedError | Invalid Permissions | Error when trying to set up domain, usually permissions issue | | AuthCodeError | 2FA code invalid | Invalid two-factor authentication code | | EmailNotVerifiedError | Unverified email | User needs to verify email in provider account | | InvalidCredentialsError | Maximum login attempts exceeded | User should reset password or use manual setup | | InvalidDomainError | Domain not found | Domain not found in provider account | | InvalidNameservers | Invalid Nameservers | Domain nameservers are pointing to a third-party provider | | GenericError | An error occurred | Generic setup error, user should retry | | PurchaseDomainError | Purchase error | Error during purchase, refresh and retry | | RegistroDomainInTransition | Domain in transition | Registro.br transitioning to advanced DNS (wait 5 min) | | SessionError | Session Error | Session timed out, refresh page | | SpfRecordsLimitError | SPF limit error | SPF record cannot have more than 10 domains | | SpfRecordsLengthError | SPF length error | SPF record cannot exceed 255 characters | | ProviderAuthenticationError | Error in provider authentication | Authentication issue with the provider account | | ProviderError | Custom error in provider | Provider-specific error, such as a protected domain | | UserInputTimeoutError | Session timed out | User did not enter credentials or 2FA in time, start over | This list may not be exhaustive as some errors come directly from DNS providers. ## Configuration Pitfalls Common configuration mistakes that cause unexpected errors. ### userId Mismatch If you pass `userId` in your config object, it must match the `userId` used when generating the JWT token. A mismatch can cause misleading errors like "Invalid domain or DNS records". ```javascript theme={null} // ❌ Wrong: userId mismatch const token = await getToken({ userId: "user-123" }); // JWT created with user-123 entri.showEntri({ applicationId: "...", token, userId: "user-456", // Different userId in config dnsRecords: [...] }); // ✅ Correct: userId matches const token = await getToken({ userId: "user-123" }); entri.showEntri({ applicationId: "...", token, userId: "user-123", // Same userId dnsRecords: [...] }); ``` ### validateDmarc with Existing Records When `validateDmarc: true` is set and the domain already has a valid DMARC record, Entri will **not** modify it. However, if you also send a DMARC record in your `dnsRecords` array, this can trigger the manual setup flow. | Scenario | Behavior | | ---------------------------------------------------------- | ------------------------------- | | `validateDmarc: true`, no existing DMARC | Entri creates your DMARC record | | `validateDmarc: true`, valid DMARC exists | Entri skips DMARC (no changes) | | `validateDmarc: true`, DMARC in `dnsRecords`, DMARC exists | May trigger manual setup flow | This is intentional to prevent conflicts with existing email authentication. If you're seeing unexpected manual flow redirects, check if the domain already has DMARC configured. ## Debugging Tips ### Check the Network Tab 1. Open DevTools → Network tab 2. Clear logs, then run the failing Entri setup 3. Look for the `config` or `records` API call 4. Check the Response tab for error details ### Non-blocking Console Warnings Some console messages are warnings that don't block the flow: ``` Invalid hex color whiteLabel.theme.fg: #xyz ``` These indicate configuration issues but the modal will still open. ### Generating a HAR File When reporting issues to support, a HAR (HTTP Archive) file captures all network requests and helps diagnose problems quickly. **Steps in Chrome:** 1. Navigate to the page where you're experiencing the issue (don't reproduce yet) 2. Open Developer Tools: Right-click → Inspect, or `Ctrl+Shift+I` (Mac: `Cmd+Option+I`) 3. Go to the **Network** tab 4. Ensure recording is active (red circle) 5. Check **Preserve log** to capture requests across page reloads 6. Click **Clear** to remove existing logs 7. Reproduce the issue 8. Right-click any request → **Save all as HAR with Content** 9. Save and attach to your support request HAR files may contain sensitive data (tokens, cookies). Review before sharing or use Entri's secure upload option. ## Need Help? If you're encountering an error not listed here, contact Entri support with: * Error message or screenshot * Browser console output * The domain you're trying to configure # Getting Started Source: https://developers.entri.com/getting-started Your first Entri integration in 5 minutes Welcome to Entri! This guide will walk you through your first integration. New to DNS? Check out [DNS Concepts](/dns-concepts) for a quick primer on record types, propagation, and common gotchas. ## 1. Set up your dashboard Before writing code, configure your application in the [Entri Dashboard](https://dashboard.entri.com): 1. Sign up or log in 2. Create an application 3. Set your display name and icon (PNG or SVG, 1:1 ratio, white or transparent background) 4. Copy your `applicationId` and `secret` Dashboard ## 2. Install Entri Choose your preferred installation method: ```html HTML (CDN) theme={null} ``` ```bash npm theme={null} npm install entrijs ``` If using npm, import it in your code: ```javascript theme={null} import Entri from 'entrijs'; ``` **Subresource Integrity (SRI):** If your security policy requires SRI, load Entri via the pinned loader URL with its SHA-384 hash: ```html theme={null} ``` The loader at `entri-latest.js` is immutable, so the integrity hash above stays valid across Entri SDK releases — no need to update it on each version. Because the loader fetches the SDK asynchronously, `window.entri` isn't available immediately. Wait for the `entri:ready` event before calling `showEntri()`: ```javascript Callback theme={null} function onEntriReady(cb) { if (window.entri) return cb(window.entri); window.addEventListener("entri:ready", () => cb(window.entri), { once: true }); } onEntriReady((entri) => { entri.showEntri(/* …config… */); }); ``` ```javascript Promise theme={null} const entriReady = new Promise((resolve) => { if (window.entri) return resolve(window.entri); window.addEventListener("entri:ready", () => resolve(window.entri), { once: true }); }); entriReady.then((entri) => entri.showEntri(/* …config… */)); ``` ## 3. Get a JWT token For security, fetch the JWT on your **server-side**. The token expires after 60 minutes. Never expose your `secret` in client-side code. Always fetch the JWT from your backend. ```javascript JavaScript theme={null} fetch('https://api.goentri.com/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ applicationId: "YOUR_APP_ID", // From the dashboard secret: "YOUR_SECRET" // From the dashboard }), }) .then(response => response.json()) .then(data => { console.log('Token:', data.auth_token); // Save this token to use in step 4 }) .catch(error => console.error('Error:', error)); ``` ```python Python theme={null} import requests import json url = "https://api.goentri.com/token" payload = { "applicationId": "YOUR_APP_ID", # From the dashboard "secret": "YOUR_SECRET" # From the dashboard } headers = {"Content-Type": "application/json"} response = requests.post(url, headers=headers, data=json.dumps(payload)) if response.ok: data = response.json() print("Token:", data["auth_token"]) # Save this token to use in step 4 else: print("Error:", response.text) ``` ## 4. Launch the Entri modal With your token ready, launch the Entri modal: ```javascript theme={null} entri.showEntri({ applicationId: "YOUR_APP_ID", token: "YOUR_JWT_TOKEN", // From step 3 dnsRecords: [ { type: "CNAME", host: "www", value: "your-app.com", ttl: 300 } ] }); ``` MX records require a `priority` field. See [DNS Records](/connect/dns-records) for all record types. ## 5. Handle events Listen for completion and close events: ```javascript theme={null} entri.showEntri({ applicationId: "YOUR_APP_ID", token: "YOUR_JWT_TOKEN", dnsRecords: [...], // Called when DNS setup completes successfully onSuccess: (data) => { console.log('Domain configured:', data.domain); // { domain: "example.com", success: true, setupType: "automatic", ... } }, // Called when the modal is closed onEntriClose: (data) => { console.log('Modal closed:', data); } }); ``` ## Complete example: React Here's a full working example with React: ```jsx theme={null} import { useEffect, useState } from 'react'; function DomainSetup() { const [token, setToken] = useState(null); useEffect(() => { // Fetch JWT from your backend fetch('/api/entri-token') .then(res => res.json()) .then(data => setToken(data.token)); }, []); const handleSetup = () => { if (!token) return; entri.showEntri({ applicationId: "YOUR_APP_ID", token: token, dnsRecords: [ { type: "CNAME", host: "www", value: "your-app.com", ttl: 300 } ], onSuccess: (data) => { console.log('Success:', data); // Update your UI or backend } }); }; return ( ); } export default DomainSetup; ``` For more framework-specific examples, see our [React Integration Guide](/integration/react) and [Vanilla JS Guide](/integration/vanilla-js). ## Next steps Now that you have the basics working, explore Entri's products: Automated DNS configuration for 60+ providers Let users purchase domains in your app Automatic SSL certificate provisioning Custom domain management for your users ## Dive deeper All showEntri() options Complete events documentation Server-side notifications Understanding DNS records ## Need help? We're here to help! Email us at [support@entri.com](mailto:support@entri.com) or reach out to your account manager. # Handling DKIM, SPF, and DMARC Records Source: https://developers.entri.com/handling-dkim-spf-dmarc-records Add and manage DKIM, SPF, and DMARC email-authentication records through Entri. ## DKIM records DKIM (DomainKeys Identified Mail) is an email security standard designed to make sure messages aren't altered in transit between the sending and recipient servers. If a DKIM record is added to a domain, it increases the deliverability of emails. ### Sending mail directly If your application sends mail directly, you can simply add a DKIM record to your configuration object as a TXT record (see [Getting Started](/getting-started)), e.g.: ```Java Text theme={null} const config = { applicationId: "12345", dnsRecords: [ { type: "TXT", host: "cool-email.example.com", value: "v=DKIM1; p=76E629F05F70 9EF665853333 EEC3F5ADE69A 2362BECE4065 8267AB2FC3CB 6CBE", ttl: 300, }, ], }; ``` ### Sending mail through a 3rd party If your application sends email using a 3rd party service configured by your customer (e.g. Google Workspace, Zoho Mail, or Microsoft 365), your Entri account manager can enable a DKIM prompt that walks your customers through setting up DKIM using those services. To turn this on, add the [`enableDkim`](/connect/configuration) key to your configuration object: ```Java Text theme={null} const config = { applicationId: "12345", enableDkim: true, dnsRecords: [ // ... ], }; ``` Once DKIM is enabled for your application, the Entri modal will detect what email provider a domain uses (Entri supports Google Workspace, Microsoft 365, Zoho Mail). Depending on the provider, Entri will show instructions for how to set up DKIM after the other types of records have been added. If Entri detects that DKIM has already been set up, we will skip this step. For example: 664 Entri UI for Assisting Google Workspace customers with DKIM setup ## SPF records **Important** When providing Entri the SPF record using the script found on the [Installing Entri](/getting-started) page, the record type must be identified as TXT. When providing Entri the desired SPF record, please include the full SPF record. For example: ```Text Text theme={null} v=spf1 include:u123456.wl.sendgrid.net -all ``` If the user has an existing SPF record, Entri will automatically append the new SPF record inside the users existing SPF record. For example: **Record sent to Entri:** ```Text Text theme={null} v=spf1 a mx include:u826348.wl.sendgrid.net -all ``` **Existing record:** ```Text Text theme={null} v=spf1 a mx include:\_spf.google.com include:spf.protection.outlook.com -all ``` **Updated Record:** ```Text Text theme={null} v=spf1 a mx include:\_spf.google.com include:spf.protection.outlook.com include:u826348.wl.sendgrid.net -all ``` **Important** If Entri compares the existing record with the record from the end-user and there’s a conflict with the appendix (\~all vs -all) then we will use \~all by default. \~all is recommended as it minimizes SPF errors. ### How GoDaddy Uses Nested Includes for SPF When handling SPF records through Domain Connect, GoDaddy follows a strategy that avoids directly modifying the root domain’s existing SPF record. Instead, GoDaddy creates a dedicated subdomain (typically in the form of `dc-._spfm.`) and publishes its SPF configuration there. This subdomain contains the necessary mechanisms, such as ip4, ip6, or additional include statements, to authorize GoDaddy’s sending infrastructure. The main SPF record on the root domain is then updated to reference this subdomain using an include mechanism (e.g., `include:dc-._spfm.)`. This approach creates a nested include, where the parent SPF record delegates authorization to GoDaddy via the subdomain. This method helps keep the root SPF record cleaner, allows modular updates to the provider-specific SPF content, and reduces the risk of accidentally breaking existing SPF configurations. For example, consider a domain `mydomain.com` that initially has the following SPF record: ```Text Text theme={null} TXT @ v=spf1 include:mail.sampledomain.com ~all ``` Then, a new record is submitted to Entri: ```Text Text theme={null} TXT @ v=spf1 include:newvendor.com ~all ``` GoDaddy will merge both records using a nested include, resulting in: ```Text Text theme={null} TXT @ v=spf1 include:mail.sampledomain.com include:dc-bbalbf040d._spfm.mydomain.com ~all TXT dc-bba1bf040d._spfm v=spf1 include:newvendor.com ~all ``` ### GoDaddy SPF Records and Webhooks Our propagation check process is designed to account for nested SPF structures, such as those used by GoDaddy through Domain Connect. When verifying record propagation, Entri Connect will follow include chains and subdomain delegations to ensure that the original SPF record specified in the showEntri configuration is present and resolvable under the domain. Continuing the example above, even though GoDaddy published the record as: ```Text Text theme={null} TXT @ v=spf1 include:mail.sampledomain.com include:dc-bbalbf040d._spfm.mydomain.com ~all TXT dc-bba1bf040d._spfm v=spf1 include:newvendor.com ~all ``` The webhook notification will surface it in a simplified form, reflecting the original intent of the configuration: ```Text Text theme={null} { "data": { "records_non_propagated": [], "records_propagated": [ { "type": "TXT", "host": "@", "value": "v=spf1 include:mail.sampledomain.com ~all" } ] } //... } ``` ## DMARC records To set up DMARC records for your users, include a TXT record with `_dmarc` as the host value in your configuration object (see [Installation](/getting-started)), e.g. ```JavaScript JavaScript theme={null} const config = { applicationId: "12345", dnsRecords: [ { type: "TXT", host: "_dmarc", value: "v=DMARC1; p=none; rua=mailto:dmarc-reports@solarmora.com", ttl: 300, }, ], }; ``` #### Optional: Check for existing DMARC record If you want to check if your user has a valid DMARC record already set, and if so, **not** update it, you can set the `validateDmarc` property to `true` in your `config`. **Need Advanced DMARC handling?** Check out our [DMARC Handling: Advanced Options](/advanced-dmarc-options) section! # React Integration Source: https://developers.entri.com/integration/react How to integrate Entri with React applications Learn how to integrate Entri into your React application with proper event handling and state management. ## Prerequisites * Your `applicationId` from the Entri dashboard * A valid JWT token fetched from your server * Basic understanding of React hooks ## Installation ```bash theme={null} npm install entrijs ``` Import the functions you need: ```jsx theme={null} import { showEntri, close, checkDomain } from 'entrijs'; ``` NPM functions are **async** - the package automatically loads the Entri SDK when first called. ### Quick Start (NPM) ```jsx theme={null} import { useEffect } from 'react'; import { showEntri } from 'entrijs'; function DomainConnect({ applicationId, token }) { useEffect(() => { const handleClose = (event) => { console.log('Entri closed:', event.detail); }; window.addEventListener('onEntriClose', handleClose); return () => window.removeEventListener('onEntriClose', handleClose); }, []); const handleConnect = async () => { await showEntri({ applicationId, token, dnsRecords: [ { type: 'CNAME', host: 'www', value: 'your-service.com', ttl: 300, }, ], }); }; return ; } ``` ### Custom Hook (NPM) ```jsx theme={null} // hooks/useEntri.js import { useState, useEffect, useCallback } from 'react'; import { showEntri, close } from 'entrijs'; export function useEntri() { const [isOpen, setIsOpen] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); useEffect(() => { const handleSuccess = (event) => { setResult(event.detail); }; const handleClose = (event) => { setIsOpen(false); if (event.detail?.error) { setError(event.detail.error); } }; window.addEventListener('onSuccess', handleSuccess); window.addEventListener('onEntriClose', handleClose); return () => { window.removeEventListener('onSuccess', handleSuccess); window.removeEventListener('onEntriClose', handleClose); }; }, []); const openEntri = useCallback(async (config) => { setIsOpen(true); setError(null); setResult(null); await showEntri({ applicationId: config.applicationId, token: config.token, dnsRecords: config.dnsRecords, prefilledDomain: config.prefilledDomain, userId: config.userId, }); }, []); const closeEntri = useCallback(async () => { await close(); }, []); return { openEntri, closeEntri, isOpen, result, error, }; } ``` ### With TypeScript (NPM) The package exports types you can use: ```typescript theme={null} import { showEntri, close, type EntriConfig, type EntriCloseEventDetail, type EntriSuccessEventDetail, } from 'entrijs'; ``` Add to your `index.html`: ```html theme={null} ``` The SDK will be available globally as `window.entri`. ### Quick Start (Script Tag) ```jsx theme={null} import { useEffect } from 'react'; function DomainConnect({ applicationId, token }) { useEffect(() => { const handleClose = (event) => { console.log('Entri closed:', event.detail); }; window.addEventListener('onEntriClose', handleClose); return () => window.removeEventListener('onEntriClose', handleClose); }, []); const handleConnect = () => { window.entri.showEntri({ applicationId, token, dnsRecords: [ { type: 'CNAME', host: 'www', value: 'your-service.com', ttl: 300, }, ], }); }; return ; } ``` ### Custom Hook (Script Tag) ```jsx theme={null} // hooks/useEntri.js import { useState, useEffect, useCallback } from 'react'; export function useEntri() { const [isOpen, setIsOpen] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); useEffect(() => { const handleSuccess = (event) => { setResult(event.detail); }; const handleClose = (event) => { setIsOpen(false); if (event.detail?.error) { setError(event.detail.error); } }; window.addEventListener('onSuccess', handleSuccess); window.addEventListener('onEntriClose', handleClose); return () => { window.removeEventListener('onSuccess', handleSuccess); window.removeEventListener('onEntriClose', handleClose); }; }, []); const openEntri = useCallback((config) => { setIsOpen(true); setError(null); setResult(null); window.entri.showEntri({ applicationId: config.applicationId, token: config.token, dnsRecords: config.dnsRecords, prefilledDomain: config.prefilledDomain, userId: config.userId, }); }, []); const closeEntri = useCallback(() => { window.entri.close(); }, []); return { openEntri, closeEntri, isOpen, result, error, }; } ``` ### With TypeScript (Script Tag) Define your own types or use the ones from the NPM package: ```typescript theme={null} interface EntriConfig { applicationId: string; token: string; dnsRecords: DNSRecord[]; prefilledDomain?: string; userId?: string; } interface DNSRecord { type: 'A' | 'AAAA' | 'CNAME' | 'TXT' | 'MX' | 'NS'; host: string; value: string; ttl: number; priority?: number; } interface EntriCloseEventDetail { domain: string | null; success: boolean; setupType: 'automatic' | 'manual' | 'sharedLogin' | 'purchase' | null; provider: string; lastStatus: string; error?: { code: string; title: string; details: string; }; } interface EntriSuccessEventDetail { domain: string; success: true; setupType: 'automatic' | 'manual'; provider: string; jobId: string; } // Extend Window type declare global { interface Window { entri: { showEntri: (config: EntriConfig) => void; close: () => void; checkDomain: (domain: string, config: EntriConfig) => Promise; }; } } ``` ## Using the Hook Once you have the hook, use it in your components: ```jsx theme={null} // components/DomainSetup.jsx import { useEntri } from '../hooks/useEntri'; export function DomainSetup({ applicationId, token, userId }) { const { openEntri, isOpen, result, error } = useEntri(); const handleConnect = () => { openEntri({ applicationId, token, userId, dnsRecords: [ { type: 'CNAME', host: 'www', value: 'your-service.com', ttl: 300, }, ], }); }; if (result?.success) { return (
Domain {result.domain} connected successfully!
); } return (
{error && (

Error: {error.title || 'An error occurred'}

)}
); } ``` ## Event Handling Entri emits events via `window`. Set up listeners in `useEffect` **before** calling `showEntri()`. Events work the same way for both NPM and Script Tag installations. ### onSuccess Triggered when the user successfully completes domain setup. ```jsx theme={null} useEffect(() => { const handleSuccess = (event) => { const { domain, setupType, provider, jobId } = event.detail; console.log(`Domain ${domain} configured via ${setupType}`); // Store jobId for webhook correlation }; window.addEventListener('onSuccess', handleSuccess); return () => window.removeEventListener('onSuccess', handleSuccess); }, []); ``` ### onEntriClose Triggered when the modal is closed, whether successful or not. ```jsx theme={null} useEffect(() => { const handleClose = (event) => { const { domain, success, setupType, lastStatus, error } = event.detail; if (success) { console.log(`${domain} configured successfully`); } else if (error) { console.log(`Error: ${error.code} - ${error.title}`); } else { console.log(`User exited at: ${lastStatus}`); } }; window.addEventListener('onEntriClose', handleClose); return () => window.removeEventListener('onEntriClose', handleClose); }, []); ``` ### onEntriStepChange Triggered when the user moves between screens. Useful for analytics. ```jsx theme={null} useEffect(() => { const handleStepChange = (event) => { const { step, domain, provider } = event.detail; analytics.track('entri_step', { step, domain, provider }); }; window.addEventListener('onEntriStepChange', handleStepChange); return () => window.removeEventListener('onEntriStepChange', handleStepChange); }, []); ``` ### onEntriManualSetupDocumentationClick Triggered when the user clicks the manual setup guide link. ```jsx theme={null} useEffect(() => { const handleManualClick = () => { openSupportChat(); }; window.addEventListener('onEntriManualSetupDocumentationClick', handleManualClick); return () => window.removeEventListener('onEntriManualSetupDocumentationClick', handleManualClick); }, []); ``` ## Troubleshooting ### Event listeners not firing Make sure you're adding listeners **before** calling `showEntri()`. In React, use `useEffect` to set up listeners on mount: ```jsx theme={null} // ✅ Correct - listeners added on mount via useEffect useEffect(() => { window.addEventListener('onEntriClose', handler); return () => window.removeEventListener('onEntriClose', handler); }, []); // ❌ Wrong - listener added after showEntri const handleClick = () => { showEntri(config); // or window.entri.showEntri(config) window.addEventListener('onEntriClose', handler); // Too late! }; ``` ### Stale state in event handlers React's closures can capture stale state. Use refs or functional updates: ```jsx theme={null} // ✅ Use functional update to avoid stale state setResult(prevResult => ({ ...prevResult, ...event.detail })); // ✅ Or use a ref for values needed in event handlers const configRef = useRef(config); useEffect(() => { configRef.current = config; }, [config]); ``` ### Multiple event handlers firing Ensure you clean up listeners when the component unmounts: ```jsx theme={null} useEffect(() => { const handler = (event) => { /* ... */ }; window.addEventListener('onEntriClose', handler); return () => { window.removeEventListener('onEntriClose', handler); }; }, []); ``` ## Focus Trap Conflicts When using Entri with React UI libraries that implement their own focus management (Reach UI Dialog, Radix UI), you may experience focus conflicts where tab navigation breaks. ### The Problem Both Entri and your modal library compete for focus control. Common symptoms: * Tab key not cycling through elements correctly * Focus jumping unexpectedly * Screen reader navigation issues ### Solution 1: Embedded Mode (Recommended) Mount Entri inside your existing dialog using embedded mode: ```jsx theme={null} import { Dialog, DialogContent } from '@reach/dialog'; import { useEffect, useState } from 'react'; function DomainSetupDialog() { const [isOpen, setIsOpen] = useState(false); useEffect(() => { if (!isOpen) return; window.entri.showEntri({ applicationId: 'your-app-id', token: 'your-jwt-token', dnsRecords: [/* your records */], whiteLabel: { customProperties: { general: { embeddedMode: { selector: '#entri-container', containerStyles: { boxShadow: 'none', borderRadius: '0px', width: '100%', height: '100%', }, }, }, }, }, }); }, [isOpen]); return ( setIsOpen(false)}>
); } ``` ### Solution 2: Disable Focus Lock When Entri is Active Use a state flag to disable your focus trap while Entri is open: ```jsx theme={null} import FocusLock from 'react-focus-lock'; import { useState, useEffect } from 'react'; function CustomModal() { const [entriActive, setEntriActive] = useState(false); useEffect(() => { const handleClose = () => setEntriActive(false); window.addEventListener('onEntriClose', handleClose); return () => window.removeEventListener('onEntriClose', handleClose); }, []); const launchEntri = () => { setEntriActive(true); window.entri.showEntri({ /* config */ }); }; return ( ); } ``` This issue has been observed with Reach UI Dialog specifically, but may occur with other libraries that implement aggressive focus locking. ## Next Steps * [Configuration Reference](/connect/configuration) - Full configuration options * [Webhooks](/webhooks-overview) - Server-side event handling * [Getting Started](/getting-started) - Dashboard setup and token generation # Vanilla JavaScript Integration Source: https://developers.entri.com/integration/vanilla-js How to integrate Entri with plain JavaScript applications Learn how to integrate Entri into any web application using plain JavaScript, without frameworks or build tools. ## Prerequisites * Your `applicationId` from the Entri dashboard * A valid JWT token fetched from your server * Basic understanding of JavaScript and DOM events ## Installation Add the Entri SDK to your HTML page: ```html theme={null} ``` The SDK will be available globally as `window.entri`. ## Quick Start A minimal working example to get started quickly: ```html theme={null} Domain Setup ``` ## Full Implementation ### Step 1: Create an Entri Manager Encapsulate all Entri logic in a reusable module for cleaner code. ```javascript theme={null} // entri-manager.js const EntriManager = (() => { let isOpen = false; let onSuccessCallback = null; let onCloseCallback = null; let onStepChangeCallback = null; // Set up event listeners once window.addEventListener('onSuccess', (event) => { if (onSuccessCallback) { onSuccessCallback(event.detail); } }); window.addEventListener('onEntriClose', (event) => { isOpen = false; if (onCloseCallback) { onCloseCallback(event.detail); } }); window.addEventListener('onEntriStepChange', (event) => { if (onStepChangeCallback) { onStepChangeCallback(event.detail); } }); return { open(config) { isOpen = true; window.entri.showEntri({ applicationId: config.applicationId, token: config.token, dnsRecords: config.dnsRecords, prefilledDomain: config.prefilledDomain, userId: config.userId, }); }, close() { window.entri.close(); }, isOpen() { return isOpen; }, onSuccess(callback) { onSuccessCallback = callback; }, onClose(callback) { onCloseCallback = callback; }, onStepChange(callback) { onStepChangeCallback = callback; }, }; })(); ``` ### Step 2: Use in Your Application ```html theme={null} Domain Setup
``` ## Event Handling Entri emits several events during the flow. Set up listeners **before** calling `showEntri()`. ### onSuccess Triggered when the user successfully completes domain setup (reaches the Congratulations screen). ```javascript theme={null} window.addEventListener('onSuccess', (event) => { const { domain, setupType, provider, jobId } = event.detail; console.log('Domain:', domain); console.log('Setup type:', setupType); // 'automatic' or 'manual' console.log('Provider:', provider); console.log('Job ID:', jobId); // Use for webhook correlation }); ``` ### onEntriClose Triggered when the modal is closed, whether successful or not. ```javascript theme={null} window.addEventListener('onEntriClose', (event) => { const { success, domain, error, lastStatus } = event.detail; if (success) { console.log('Domain configured successfully:', domain); } else if (error) { console.log('Error:', error.code, '-', error.title); } else { console.log('User exited at:', lastStatus); } }); ``` ### onEntriStepChange Triggered when the user moves between screens in the modal. Useful for analytics. ```javascript theme={null} window.addEventListener('onEntriStepChange', (event) => { const { step, domain, provider } = event.detail; // Track user progress console.log('Step:', step); console.log('Domain:', domain); console.log('Provider:', provider); }); ``` ### onEntriManualSetupDocumentationClick Triggered when the user clicks the manual setup guide link. ```javascript theme={null} window.addEventListener('onEntriManualSetupDocumentationClick', () => { // Open your custom help, e.g., a support chat openSupportChat(); }); ``` ## Common Patterns ### Loading State UI ```javascript theme={null} const button = document.getElementById('connect-btn'); const originalText = button.textContent; const setLoading = (isLoading) => { button.disabled = isLoading; button.textContent = isLoading ? 'Connecting...' : originalText; }; // When opening Entri setLoading(true); window.entri.showEntri(config); // When Entri closes window.addEventListener('onEntriClose', () => { setLoading(false); }); ``` ### Error Display ```javascript theme={null} const showError = (error) => { const errorDiv = document.getElementById('error-message'); if (!error) { errorDiv.style.display = 'none'; return; } errorDiv.innerHTML = `${error.title}

${error.details}

`; errorDiv.style.display = 'block'; }; window.addEventListener('onEntriClose', (event) => { if (event.detail.error) { showError(event.detail.error); } }); ``` ### Check Domain Before Opening Use `checkDomain` to verify provider support before showing the modal: ```javascript theme={null} const connectDomain = async (domain) => { const config = { applicationId: 'YOUR_APPLICATION_ID', token: 'YOUR_JWT_TOKEN', dnsRecords: [ { type: 'CNAME', host: 'www', value: 'your-service.com', ttl: 300 } ], }; // Check if automatic setup is supported const check = await window.entri.checkDomain(domain, config); if (check.setupType === 'Automatic') { console.log('Automatic setup available with', check.provider); } else { console.log('Manual setup required'); } // Open Entri with prefilled domain config.prefilledDomain = domain; window.entri.showEntri(config); }; ``` ### Multiple DNS Records Configure multiple records for complex setups: ```javascript theme={null} window.entri.showEntri({ applicationId: 'YOUR_APPLICATION_ID', token: 'YOUR_JWT_TOKEN', dnsRecords: [ { type: 'CNAME', host: 'www', value: 'your-service.com', ttl: 300, }, { type: 'TXT', host: '@', value: 'v=spf1 include:your-service.com ~all', ttl: 300, }, { type: 'MX', host: '@', value: 'mail.your-service.com', ttl: 300, priority: 10, }, ], }); ``` ## Troubleshooting ### Event listeners not firing Make sure you're adding listeners **before** calling `showEntri()`: ```javascript theme={null} // Wrong - listener added after showEntri window.entri.showEntri(config); window.addEventListener('onEntriClose', handler); // Too late! // Correct - listener added before showEntri window.addEventListener('onEntriClose', handler); window.entri.showEntri(config); ``` ### SDK not loaded Check that the script has loaded before using it: ```javascript theme={null} if (typeof window.entri === 'undefined') { console.error('Entri SDK not loaded'); return; } window.entri.showEntri(config); ``` Or wait for the script to load: ```javascript theme={null} window.addEventListener('load', () => { // Safe to use window.entri here document.getElementById('connect-btn').addEventListener('click', () => { window.entri.showEntri(config); }); }); ``` ### Multiple event handlers firing If you're dynamically adding/removing elements, make sure to clean up event listeners: ```javascript theme={null} const handler = (event) => { console.log('Entri closed:', event.detail); }; // Add listener window.addEventListener('onEntriClose', handler); // Remove listener when no longer needed window.removeEventListener('onEntriClose', handler); ``` ## Next Steps * [API Reference](/connect/configuration#entrishowentriconfig) - Full configuration options * [Webhooks](/webhooks-overview) - Server-side event handling * [Getting Started](/getting-started) - Dashboard setup and token generation # Monitor Configuration Source: https://developers.entri.com/monitor/configuration showEntri() config keys for Monitor: top-level monitor: true and per-record monitor: true. ## Adding monitoring using Entri Connect When initializing a new domain using Entri Connect, you can pass a `monitor: true` property in either the domain object or each record object to specify that it should be tracked with Entri Monitor. Monitor everything: ```json theme={null} { "prefilledDomain": "mydomain123.com", "applicationId": "12345", "token": "12345", "monitor": true, "dnsRecords": [ { "type": "CNAME", "host": "{SUBDOMAIN}.xyz.example.com", // blog.xyz.example.com "value": "custom-proxy.leadpages.net", "ttl": 300, }, { "type": "TXT", "host": "@", "value": "{SLD}-{TLD}", // example-com "ttl": 300, } ] } ``` Monitor only the CNAME record: ```json theme={null} { "prefilledDomain": "mydomain123.com", "applicationId": "12345", "token": "12345", "dnsRecords": [ { "type": "CNAME", "host": "{SUBDOMAIN}.xyz.example.com", // blog.xyz.example.com "value": "custom-proxy.leadpages.net", "ttl": 300, "monitor":true }, { "type": "TXT", "host": "@", "value": "{SLD}-{TLD}", // example-com "ttl": 300, } ] } ``` # Monitor Endpoints Source: https://developers.entri.com/monitor/endpoints REST endpoints for Entri Monitor: /monitor/domains, /monitor/domains/batch, /monitor/health, /monitor/status. ## Entri Monitor API ## General API Guidelines ### Base URL ``` https://api.goentri.com ``` ### Authentication All requests require the following headers: * **`applicationId`**: Your unique application ID. * **`Authorization`**: A bearer token generated using your client secret. **Example Headers**: ```http theme={null} applicationId: your-app-id Authorization: Bearer your-auth-token ``` Monitor endpoints are protected by a **custom Lambda authorizer** that validates the `applicationId` + `Authorization` pair on every request. Authorizer results are **not cached** — each request is re-authorized — so always send a valid, unexpired token. ## Quickstart ### Step 1: Add domains for monitoring You'll first need to tell Entri Monitor which domain you'd like to monitor. Send a `POST` request to `/monitor/domains` with the name of the domain, as well as a list of the records you want to monitor, including their `type`, `host`, `value`, and `ttl` properties. Example request body: ```json theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "A", "host": "@", "value": "93.184.216.34", "ttl": 3600 } ] } ``` You'll recieve a response like this if you're successful: ```json theme={null} { "message": "Domain added successfully" } ``` ### Step 2: Specify a webhook URL Log into the Entri Dashboard and navigate to the App Settings page. Enter the URL of the webhook that you'll use to recieve notifications about the DNS changes you specified. ### Step 3: Set up your service to receive the webhook requests at the URL you specified Your webhook URL will be sent requests if the DNS records you specified are modified or deleted. Example request: ```json theme={null} { "id": "e98d267b-84b8-4229-a94a-1933ed7f91ea", "subdomain": "www", "domain": "example.com", "user_id": "user123", "type": "domain.record_missing" / "domain.record_restored", "data": { "records_propagated": [ { "host": "www", "ttl": 300, "type": "CNAME", "value": "mydestinationdomain.com" } ], "records_non_propagated": [ { "host": "@", "ttl": 300, "type": "TXT", "value": "domain_verification123" } ] }, "connect_link": "https://customurl.goentri.dev/share/27bc7873e6ad452780686b8c0436eb75" } ``` ## Retrieve DNS records ``` GET https://api.goentri.com/monitor/domains/:domain_name/records Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Successful response (200 status) ```JSON JSON theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ] } ``` ## Create DNS records ``` POST https://api.goentri.com/monitor/domains/:domain_name/records Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ] } ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "DNS records created successfully", "addedRecords": 1 } ``` ## Update DNS records ``` PUT https://api.goentri.com/monitor/domains/:domain_name/records Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "dnsRecords": [ { "type": "TXT", "host": "example.com", "value": "v=spf1 include:_spf.example.com ~all", "ttl": 3600 } ] } ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "DNS records updated successfully", "updatedRecords": 1 } ``` ## Delete DNS records ``` DELETE https://api.goentri.com/monitor/domains/:domain_name/records Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "dnsRecords": [ { "type": "CNAME", "host": "www.example.com", "value": "example.com", "ttl": 3600 } ] } ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "DNS records deleted successfully", "deletedRecords": 1 } ``` ## List domains ``` GET https://api.goentri.com/monitor/domains Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Successful response (200 status) ```JSON JSON theme={null} { "domains": [ { "domain": "example.com", "status": "active" } ] } ``` Query Parameters: * `offset` (integer): Pagination offset. * `limit` (integer): Number of domains per page. * `from_date` (date): Filter start date. * `to_date` (date): Filter end date. ## Retrieve domain details ``` GET https://api.goentri.com/monitor/domains/:domain_name Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Successful response (200 status) ```JSON JSON theme={null} { "domain": "example.com", "status": "active", "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ] } ``` ## Add a domain ``` POST https://api.goentri.com/monitor/domains Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ], "userId": "UUID" } ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "Domain added successfully", "domainId": "12345" } ``` ## Update a domain ``` PUT https://api.goentri.com/monitor/domains Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "domain": "example.com", "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ], "userId": "UUID" } ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "Domain updated successfully", "updatedRecords": 1 } ``` ## Delete a domain ``` DELETE https://api.goentri.com/monitor/domains Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "domain": "example.com", "subdomain": "shop", "deleteOnlyIfEmpty": true } ``` ### Request parameters | Parameter | Type | Required? | Default | Description | | ------------------- | ------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `domain` | string | Yes | N/A | The apex domain to delete from Monitor. | | `subdomain` | string | No | N/A | When provided, deletes monitoring only for the given subdomain under `domain` (for example, `shop` under `example.com`). The apex is kept intact. | | `deleteOnlyIfEmpty` | boolean | No | false | When `true`, the domain (or subdomain) is only deleted if it has **no remaining monitored DNS records**. If records still exist, the request is rejected. Useful when reconciling from multiple sources. | ### Query parameter form The same options are accepted as query parameters: ``` DELETE https://api.goentri.com/monitor/domains?domain=example.com&subdomain=shop&deleteOnlyIfEmpty=true ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "Domain deleted successfully", "domainId": "12345" } ``` ## Batch create domains Each batch request is limited to a maximum of **100 domains**. Submit multiple batches if you need to register more than 100 at once. ``` POST https://api.goentri.com/monitor/domains/batch Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Request body ```JSON JSON theme={null} { "domains": [ { "domain": "example.com", "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ] }, { "domain": "example2.com", "dnsRecords": [ { "type": "A", "host": "example.com", "value": "93.184.216.34", "ttl": 3600 } ] } ] } ``` ### Successful response (200 status) ```JSON JSON theme={null} { "message": "Batch created successfully", "batchId": "batch-12345" } ``` ## Batch status Batch status records are retained for **7 days** (TTL) after the batch is submitted. After that window, status lookups for old batches will return a not-found response. ``` GET https://api.goentri.com/monitor/domains/batch/status/:request_id Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Successful response (200 status) ```JSON JSON theme={null} { "batchId": "batch-12345", "status": "completed", "successfulDomains": 10, "failedDomains": 0 } ``` ## Webhooks To recieve webhook notifications about the target domains, log into the Entri Dashboard and navigate to the App Settings page. Enter the URL of the webhook that you'll use to recieve notifications about the DNS changes you specified. Your webhook URL will be sent requests if the DNS records you specified are modified or deleted. ### Top-Level fields * **`id`**: A unique identifier for the webhook event (e.g. `"e98d267b-84b8-4229-a94a-1933ed7f91ea"`). * **`user_id`**: The ID of the user who initiated the domain-related action (e.g. `"your-provided-user-id"`). * **`domain`**: The domain involved in the event (e.g. `"example.com"`). * **`subdomain`**: The subdomain associated with the event, if any (e.g. `"shop"`). * **`type`**: Defines the type of event. Possible values: * `"domain.record_missing"`: Used whenever there is a record missing from the records that are being monitored. * `"domain.record_restored"`: Confirms that ALL records have been restored. * **`data.records_propagated`**: Contains all the records that are being monitored and **were found correctly** configured on the domain. * **`data.records_non_propagated`**: Contains all the records that are being monitored and **were found as missing** on the domain. * **`connect_link`**: Entri Connect's sharing link with the configuration required to restablish the missing records on the domain. Example request: ```json theme={null} { "id": "e98d267b-84b8-4229-a94a-1933ed7f91ea", "subdomain": "www", "domain": "example.com", "user_id": "user123", "type": "domain.record_missing" / "domain.record_restored", "data": { "records_propagated": [ { "host": "www", "ttl": 300, "type": "CNAME", "value": "mydestinationdomain.com" } ], "records_non_propagated": [ { "host": "@", "ttl": 300, "type": "TXT", "value": "domain_verification123" } ] }, "connect_link": "https://customurl.goentri.dev/share/27bc7873e6ad452780686b8c0436eb75" } ``` # Monitor Events Source: https://developers.entri.com/monitor/events Webhook events (domain.record_missing, domain.record_restored) and the monitor_status field. This page documents the webhook events and payload fields relevant to Entri Monitor. For the payload envelope and signature verification, see [Webhooks Overview](/webhooks-overview). ## Webhook event types * `"domain.record_missing"`: Only applicable when using **Entri Monitor**. A DNS record that was previously detected is no longer found. This may indicate it was removed or is temporarily unavailable. * `"domain.record_restored"`: Only applicable when using **Entri Monitor**. A previously missing DNS record has been detected again. ## Status fields * **`monitor_status`**: Only relevant when using Entri Monitor. Describes the status of the monitoring feature. This key will not be shown on flows that don't use Entri Monitor. Possible values: * `"pending"`: Monitoring domain setup is pending. * `"success"`: Monitoring domain setup was successful. * `"failed"`: Monitoring domain setup failed. ## Example webhook payload ```json theme={null} { "id": "e98d267b-84b8-4229-a94a-1933ed7f91ea", "subdomain": "www", "domain": "example.com", "user_id": "user123", "type": "domain.record_missing", // Can also be "domain.record_restored" "data": { "records_propagated": [ { "host": "www", "ttl": 300, "type": "CNAME", "value": "mydestinationdomain.com" } ], "records_non_propagated": [ { "host": "@", "ttl": 300, "type": "TXT", "value": "domain_verification123" } ] }, "connect_link": "https://customurl.goentri.dev/share/27bc7873e6ad452780686b8c0436eb75" } ``` # Entri Monitor Overview Source: https://developers.entri.com/monitor/overview Continuous DNS record monitoring — what Entri Monitor is and when to use it. ## Overview Entri Monitor allows you to monitor DNS record changes across multiple domains. This can be useful in a variety of scenarios, such as: * Notifying your customer that they inadvertently broke the DNS record configuration required to use your application * Detecting if a customer is at risk for churn or might be moving to a competing solution If Entri Monitor detects a change (or deletion) of one of the records specified on a monitored domain, a notification is sent to the webhook URL that you provide on the App Settings page of the Entri Dashboard. Entri Monitor runs an hourly check, 24x7. Optionally, you can combine Entri Monitor with Entri Connect to help your user fix their DNS if Monitor detects a breaking change. # DNS Provider Onboarding Source: https://developers.entri.com/network/dns-provider-onboarding Register your DNS platform with the Entri Network so Entri can automatically configure DNS records on behalf of your users. ## Overview Entri Network lets DNS providers (registrars, hosting companies, DNS platforms) register their APIs with Entri so that when a mutual user launches the Entri modal, Entri can configure DNS records automatically — no copy-paste required. You configure your integration through the **Setup** wizard. It walks you through five steps: 1. **Provider profile** — how customers see you. 2. **Domain detection** — how Entri knows a domain is yours. 3. **Authentication** — how Entri logs into your API. 4. **DNS endpoints** — the routes Entri calls to read and write DNS records. 5. **Review & submit** — a final check before you go live. You can move between steps freely. Each step is saved independently, and you must complete the required fields in a step before continuing. Once you're live, this same page becomes your editable settings. This page documents every field in the wizard. *** ## Step 1 — Provider profile > **Tell us who you are.** This is the brand customers see when they connect a domain to an app. It's also how Entri reaches you if something goes sideways. ### Brand | Field | Required | Description | | ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- | | **Provider name** | ✓ | The name shown to customers in the connect dialog (e.g. `DNSimple`, `Cloudflare`) | | **Website** | — | Your company website URL | | **Support email** | ✓ | Where customers reach you for connection help | | **Logo** | — | Upload a square PNG or SVG, at least 64×64. Shown to users when they connect their domain via the Entri modal | | **Brand color** | — | Used for buttons and highlights in the connect dialog. Click the swatch to open the color picker, or enter a hex value (e.g. `#006fee`) | Your provider name may be pre-filled if Entri already has a record for your platform. You can edit it before saving. ### Manual fallback Optional — a link to your own DNS instructions, in case Entri ever needs to send a customer to you to finish a connection. | Field | Required | Description | | --------------------------- | -------- | ---------------------------------------------------------------------- | | **Manual instructions URL** | — | A link to your DNS help docs (e.g. `https://help.yourcompany.com/dns`) | ### Preview Use **Preview in Entri** to see a snapshot of the provider card customers will see in the Entri picker. *** ## Step 2 — Domain detection > **Help Entri recognize your customers' domains.** When someone enters their domain in Entri, we need to know it's managed by you. Pick one or both methods — Entri tries each in order. ### Nameserver match If a domain's nameservers are yours, Entri knows it's on your service. | Field | Required | Description | | -------------------- | -------- | ----------------------------------------------------------------------------------------------------------- | | **Your nameservers** | ✓ | One nameserver hostname per line (e.g. `ns1.example.com`). Wildcards (e.g. `ns*.example.com`) are supported | ### DNS lookup (recommended) Add a DNS record Entri can verify on every domain you manage. Useful if your nameservers vary. | Field | Required | Description | | ------------------- | -------- | ----------------------------------------------------------------------------- | | **DNS Record Type** | ✓ | The record type Entri checks: `TXT` (recommended), `CNAME`, `A`, or `MX` | | **Record host** | ✓ | The hostname Entri looks up (e.g. `_entri-verify`) | | **Expected value** | ✓ | The value Entri expects to find at that host (e.g. `entri-verify=your-token`) | DNS lookup is the most reliable method when your nameservers aren't fixed. Configure your platform to add the record above to every domain it manages. *** ## Step 3 — Authentication > **Decide how Entri logs into your API.** You configure exactly **one** method per integration. ### Method Choose one of the three authentication methods: | Method | Description | | ----------------------------- | ----------------------------------------------------------------------- | | **OAuth 2.0** | Recommended. The customer signs in to your dashboard to authorize Entri | | **Login (with optional MFA)** | The customer enters their username + password directly in Entri | | **Basic Auth** | An API key sent as the `Authorization` header. Simplest for read-only | ### OAuth credentials Shown when **OAuth 2.0** is selected. Find these in your OAuth app settings. | Field | Required | Description | | ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | **Client ID** | ✓ | The client ID for the Entri OAuth application registered in your system | | **Client secret** | ✓ | The corresponding client secret — stored encrypted with AWS KMS | | **Authorization URL** | ✓ | The OAuth authorization endpoint your users are redirected to | | **Token URL** | ✓ | The token exchange endpoint | | **Scopes** | — | Comma-separated OAuth scopes. Leave blank if your provider doesn't use scopes | | **Entri redirect URI** | (read-only) | Entri's callback URI (e.g. `https://app.entri.com/auth/callback/`). Use **Copy** and add it to your OAuth app's allowed callback URLs | With OAuth 2.0, Entri manages token injection automatically — you don't need to declare Authentication Parameters. Once the customer approves access, Entri sends the access token as `Authorization: Bearer ` on every subsequent API call. ### Authentication Parameters Declare every value Entri injects into HTTP calls during authentication — the request field name, its location, and the static encrypted value Entri sends. Use **Add parameter** to open the drawer. | Field | Required | Description | | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------ | | **Request Field Name** | ✓ | The header, query, body field name, or URL variable for path segments (e.g. `X-API-Key`) | | **Location** | ✓ | Where the value is placed: `header`, `query`, `body`, or `url` | | **Prefix** | — | A string prepended to the value before injection (e.g. `Bearer ` to produce `Authorization: Bearer `) | | **Encrypted Value** | ✓ | The fixed value — the same for every user, stored encrypted at rest (e.g. `encrypted:kms:arn:aws:kms:...`) | Authentication Parameters here carry **static** platform-level credentials (like an API key) that accompany every authentication call. OAuth 2.0 does not need them. ### Request signing Shown for **Login** and **Basic Auth**. Optional — configure this if you need to verify that signed requests originated from the Entri platform. Entri signs the request body with its global RSA private key; you verify using Entri's public key. | Field | Description | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **Algorithm** | Signing algorithm — `SHA256withRSA` (read-only) | | **Signature header** | HTTP header where Entri attaches the base64-encoded signature (e.g. `Sig`, `X-Entri-Signature`) | | **Our public key ref (KMS ARN)** | KMS ARN Entri uses to export its public key (`kms:GetPublicKey`). Share the resulting key material with your platform so it can verify signatures | Request signing is not applicable to OAuth 2.0. *** ## Step 4 — DNS endpoints > **Wire up your DNS API.** Map each operation Entri needs to a route in your API. Complete every required row in the sidebar before you continue. The sidebar lists the operations you must configure: ### List domains `get_domains` — Returns all domains for the authenticated customer. Entri calls this to populate the domain picker. The response should be a list of domains. Map your domain identifier and name fields to Entri's standard names in **Field mappings** if your API uses different names. ### Read DNS records `get_records` — Returns all DNS records for one domain. Entri calls this to read the current record state before making changes. Use `{variableName}` in the URL for the domain identifier returned by **List domains**. Map your record fields to Entri's standard names in **Field mappings** if needed. ### Record writes Choose how your API creates and removes DNS records: * **Separate** — configure **Add record** (`add_record`) and **Delete record** (`delete_record`) as individual endpoints. * **Batch** — configure a single **Batch DNS records** (`batch_records`) endpoint that creates or updates multiple records in one call. You only configure the rows for the shape you select. ### Update nameservers `update_nameservers` — Replaces the nameservers for a domain. Entri calls this when delegating DNS management to your platform. ### Add WWW redirect `add_www_redirect` — Creates a `www → root` redirect (or `root → www`). Used when a user points their domain to an application and needs web forwarding. ### Shared parameters Values injected into **every** DNS endpoint call — sourced from the auth response or a static encrypted value. Use **Add parameter** to open the drawer. | Field | Required | Description | | ---------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | **Request Field Name** | ✓ | The header, query, body field name, or URL variable name for path segments | | **Location** | ✓ | Where the value is placed: `header`, `query`, `body`, or `url` | | **Prefix** | — | A string prepended to the value before injection | | **Source** | ✓ | `auth` — extracted from the token/auth response via a JSON path. `static` — a fixed encrypted value, the same for every user | | **JSON Path** | ✓ (if `source: auth`) | JSONPath to extract from the authentication response (e.g. `$.accessToken`) | | **Static Value** | ✓ (if `source: static`) | The fixed value, stored encrypted — the same for every user | *** ## Endpoint configuration Every DNS endpoint is described using the same set of fields. ### HTTP Request | Field | Required | Description | | --------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Method** | ✓ | HTTP method. Read endpoints (`get_domains`, `get_records`) are `GET`; write endpoints offer `POST`, `PUT`, `PATCH`, and (where valid) `DELETE` | | **API URL** | ✓ | Full API URL. Use `{variableName}` for path variables — e.g. `https://api.yourcompany.com/v1/domains/{domainId}/records` | | **Static Query Parameters** | — | Fixed query parameters appended to every request. Pagination parameters are managed automatically — don't add them here | | **Request Headers** | — | Headers added to every call. `Authorization` is injected automatically from your auth parameters | ### Request Body Shown for non-`GET` methods. JSON only. Use `{{variableName}}` for template variables — values from the domain detection step or auth response are injected at call time. For example: ```json theme={null} { "name": "{{host}}", "type": "A" } ``` ### Response Mapping For **read** endpoints (`get_domains`, `get_records`), tell Entri how to read your API's response: | Field | Required | Description | | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | | **Data Path** | ✓ | JSONPath to the list/array in your response — e.g. `$.records`, `$.data.items`, or `$` if the response is the array itself | | **Error Path** | ✓ | JSONPath to the error message in failed responses (e.g. `$.error.message`) | For mutation endpoints (`add_record`, `delete_record`, `batch_records`, `update_nameservers`, `add_www_redirect`), only **Error Path** is shown. ### Field mappings Map your API's field names to Entri's standard names. You only need entries for fields whose names **differ** from the standard — pick the Entri field from the dropdown for each of your fields. Entri standard fields: `id`, `type`, `host`, `name`, `value`, `data`, `content`, `ttl`, `priority`, `srv.priority`, `srv.weight`, `srv.port`, `srv.target`, `mx.priority`, `mx.host`, `caa.flag`, `caa.tag`, `caa.value`. Example — if your API returns `content` instead of `value`: | Your field | Entri standard | | ---------- | -------------- | | `content` | `value` | ### Pagination If `get_domains` or `get_records` returns paginated results, configure pagination so Entri fetches all pages automatically. Choose one of: `none`, `page_based`, `cursor_based`, or `offset_based`, then fill in the parameter names for that style. Pagination parameters (page number, cursor, offset) are managed automatically by Entri. Do not add them as Static Query Parameters. *** ## Step 5 — Review & submit > **Final check.** Take one more look. When you submit, Entri's team reviews your integration and flips you live in the network — usually within one business day. The review screen summarizes everything you've configured, grouped into blocks: * **Provider** — name, website, support email, logo, brand color. * **Authentication** — method, OAuth credentials (if applicable), static auth parameters, and request-signing settings. * **Domain detection** — the approach, your nameservers, and the DNS record check. * **DNS endpoints** — the method and URL for each configured operation. If any step is incomplete, the screen lists exactly what's left to finish before you can submit. When everything is complete, choose **Submit for review**. After submission, the Entri team reviews and tests your configuration — you won't be able to make edits while it's in review. If your configuration passes, Entri publishes it into the Entri Connect modal and notifies you by email. You cannot submit until every required field across all steps is complete. *** ## Submission checklist Before submitting your integration, verify the following are configured: * [ ] **Provider profile** — provider name and support email * [ ] **Domain detection** — at least one method (nameserver match and/or DNS lookup) * [ ] **Authentication** — one method (OAuth 2.0, Login, or Basic Auth) and its required fields * [ ] **DNS endpoints** * [ ] List domains (`get_domains`) * [ ] Read DNS records (`get_records`) * [ ] Record writes — either Add record + Delete record, or Batch DNS records * [ ] Update nameservers * [ ] Add WWW redirect # Entri Network Overview Source: https://developers.entri.com/network/overview Join the Entri Network as a DNS provider so Entri can configure records on your platform automatically. ## Overview Entri Connect already configures DNS automatically for dozens of registrars and hosting platforms, and there are always more DNS providers that aren't integrated yet. Entri Network closes that gap. **Entri Network** is a self-service program that lets any DNS provider (a registrar, a hosting company, or a managed DNS platform) plug into Entri using a standardized, technology-agnostic API contract. Once you join, every service provider already using Entri can configure DNS records on your platform automatically, the same way they do with the providers Entri supports natively. You describe your API once and Entri handles the rest. There is no custom engineering work on Entri's side and no long sales cycle. You sign up, configure your endpoints in the dashboard, and your platform becomes part of the network. ## How it works 1. You sign up through the self-service onboarding flow and complete checkout. No sales call required. 2. In the Entri Network dashboard, you describe your platform: how Entri should authenticate against your API, how to detect which domains you manage, and which endpoints Entri calls to read and write DNS records. 3. Entri maps your API's request and response formats to its internal DNS logic, so your existing API works as-is with no changes on your side. 4. Once your configuration is submitted, your platform goes live in the network. When a mutual user connects a domain you manage, Entri authenticates and applies the records automatically. 5. You track every connection, view per-domain logs, and see usage analytics from your dashboard. ## Who it's for Entri Network is built for any DNS provider that wants to offer their users a seamless domain connection experience through Entri, without building and maintaining a custom integration. Whether you're a registrar, a hosting company, or a managed DNS platform, you can join the network and gain access to Entri's service providers with minimal engineering effort. The only requirement is that your platform exposes an API for reading and writing DNS records and that you're willing to map it to Entri's standard contract. Beyond that, the integration is technology agnostic. ## What you get Joining the network integrates your platform with every service provider already on Entri in just a few configuration steps. You get usage statistics across the business categories active in the network, full logs for each domain connected through your system, and recognition as a member of the Entri Network. Because everything runs through the dashboard, onboarding, configuration changes, and billing are all self-service. You stay in control of your authentication method, domain detection rules, branding, and endpoint definitions, and you can update them at any time. ## Quick links The complete field-by-field reference for registering your platform OAuth 2.0, Direct Auth Flow, and Basic Auth options The endpoints Entri calls to read and write records Everything required before you go live # Power Configuration Source: https://developers.entri.com/power/configuration showEntri() config keys for Power: power, applicationUrl, secureRootDomain, and dnsRecords with applicationUrl override. ## Configure your Entri account for Power Before you can use Entri Power, you'll need to provide some basic information on the Entri dashboard and set up your DNS records accordingly. Log into the [Entri Dashboard](https://dashboard.entri.com/), navigate to the configure section, and enter your company’s desired `cname_target`. The `cname_target` is the CNAME record that your *end-users* will eventually need to add. For example: `domains.saascompany.com` After inputting the `cname_target` on the Entri dashboard, you'll need to create a CNAME record that points your `cname_target` to `power.goentri.com` on your domain's DNS. For example, if you run `saascompany.com`and your `cname_target` is `domains.saascompany.com`, then you would add this record to the DNS for `saascompany.com`: ```JSON JSON theme={null} { type: "CNAME", host: "domains", value: "power.goentri.com", ttl: 300, } ``` After this DNS record is added, you are ready to offer custom domains to your customers! ## Power a new domain After you've configured your account, there are two methods of adding a new custom domain: utilizing the ***Entri modal***, or making a direct API request. *Please note that you'll need an active subscription to Entri Connect to use the Entri modal method. For questions about this, contact your account manager.* ## Powering a domain via the Entri Connect Modal (recommended) For more information about configuring the ***Entri modal***, see: [Create the configuration object](/connect/configuration#entrishowentriconfig). To power a custom domain via the Entri Modal, you will need to make 2 additions to the [standard configuration object](/connect/configuration#entrishowentriconfig) for the Entri Modal. * Add the property `power` and set it to `true`. * Add the property `applicationUrl` . The `applicationUrl` is the URL of your application that you would like to render on the customer's domain. This will likely change for every user. For example, the `applicationUrl` for a calendar booking app might look something like this: `https://calenderapp.com/meetings/user-name` ### Powering a root domain For powering a root domain, you just need to add an A record to the `dnsRecords` array, and the `value` as the `{ENTRI_SERVERS}` placeholder. Make sure to also add your target application destination as the `applicationUrl` key value. **Entri** will take care of the rest. If you are prompting users to enter a domain in the Entri modal, make sure that you use [dynamic configuration variables. ](/connect/dns-records#dynamic-configuration-variables) **Sample configuration:** ```JavaScript JavaScript theme={null} const config = { applicationId: "12345", // From the Entri dashboard token: mySavedToken, // The "auth_token" value generated via JWT power: true, // This enables the Entri power feature dnsRecords: [ { type: "A", host: "@", value: "{ENTRI_SERVERS}", //This will be automatically replaced for the Entri servers IPs ttl: 300, applicationUrl: "https://app.saascompany.com/page/123" } ], }; ``` Shopify domains only support a single A record at the root domain. Since this setup is not recommended, Entri automatically manages the logic for you. ### Powering a subdomain When powering a subdomain, you just need to add a CNAME record to the `dnsRecords` array. Be sure that the `value` is set to `{CNAME_TARGET}`. If you are prompting users to enter a domain in the Entri modal, make sure that you use [dynamic configuration variables. ](/connect/dns-records#dynamic-configuration-variables) **Sample configuration:** ```JavaScript JavaScript theme={null} const config = { applicationId: "12345", // From the Entri dashboard token: mySavedToken, // The "auth_token" value generated via JWT power: true, // This enables the Entri power feature dnsRecords: [ { type: "CNAME", value: "{CNAME_TARGET}", // `{CNAME_TARGET}` will automatically use the CNAME target entered in the dashboard host: "{SUBDOMAIN}", // This will use the user inputted subdomain. If hostRequired is set to true, then this will default to "www" ttl: 300, applicationUrl: "https://app.saascompany.com/page/123" } ], }; ``` **SSL Provisioned by Default** Entri Power will automatically provision an SSL certificate so there is no need to also set up Entri Secure when using Power # Power Endpoints Source: https://developers.entri.com/power/endpoints REST endpoints for Entri Power. Power endpoints are served by the Connect API gateway, not by a separate Power host. ## Power API - Custom domains ### List powered domains Partners can retrieve all domains currently **Powered** under a given `applicationId` to audit configuration, build admin UIs, and perform maintenance. This endpoint returns a paginated list of all Power-managed domains for the authenticated application. ```txt theme={null} GET https://api.goentri.com/power Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Headers | Header | Required? | Description | | ------------- | --------- | -------------------------------------------- | | Authorization | Yes | Bearer token (JWT) for authentication. | | applicationId | Yes | Your Application ID as set in the dashboard. | #### Query string parameters | Name | Type | Required? | Default | Description | | ------ | ------- | --------- | ------- | ----------------------------------------------- | | page | integer | No | 0 | Zero-based page index (0 = first page). | | size | integer | No | 50 | Max number of items to return (range: 1–200). | | status | string | No | — | Filter by power status: `active` or `inactive`. | > **Note:** `status` is optional. If omitted, the endpoint returns **all** powered domains for the application. #### Sample response ```json theme={null} { "items": [ { "domain": "www.example.com", "applicationUrl": "my.applicationurl.com", "powerRootPathAccess": ["/", "/shop"], "powerStatus": "active" }, { "domain": "blog.example.com", "applicationUrl": "my.applicationurl.com", "powerRootPathAccess": [], "powerStatus": "inactive" } ], "total": 2, "page": 0, "limit": 50, "count": 2, "pages": 1, "nextPage": null, "prevPage": null } ``` #### Field definitions * **domain** (`string`): Fully qualified domain or subdomain configured via Power. * **applicationUrl** (`string`): Current upstream URL for this domain. * **powerRootPathAccess** (`string[]`): List of root paths with access granted (may be empty). * **powerStatus** (`string`): `active` or `inactive`. * **total** (`integer`): Total number of powered domains matching the filter. * **page** (`integer`): Current zero-based page index. * **limit** (`integer`): Maximum number of items requested per page. * **count** (`integer`): Number of items returned. * **pages** (`integer`): Total number of pages available. * **nextPage** (`integer | null`): Next page index, or null if not applicable. * **prevPage** (`integer | null`): Previous page index, or null if not applicable. #### Error responses | HTTP | Example Body | When | | ---- | -------------------------------------------- | ----------------------------------------- | | 400 | `{ "message": "Invalid 'limit' parameter" }` | Validation errors on query parameters. | | 401 | `{ "message": "Unauthorized" }` | Missing/invalid JWT or `applicationId`. | | 403 | `{ "message": "Forbidden" }` | Authenticated but forbidden for this app. | | 500 | `{ "message": "Internal Server Error" }` | Unexpected server-side error. | #### Rate limits This endpoint follows the same global rate limits as other Power API endpoints. #### Example cURL ```bash theme={null} curl -X GET "https://api.goentri.com/power?status=active&size=10&page=0 \ -H "Authorization: Bearer " \ -H "applicationId: " ``` ### Check the domain's eligibility ```BASH theme={null} GET https://api.goentri.com/power?domain=www.example.com&rootDomain=false Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Parameters description | Parameter | Required? | Default | Description | | ---------- | --------- | ------- | --------------------------------------------------------------------------------- | | domain | Yes | N/A | The domain name to check | | rootDomain | No | false | true if checking the root domain's eligibilityfalse if checking for the subdomain | #### Successful response (200 status) ```JSON JSON theme={null} { // Is the domain CNAMEed to the cname_target of the application? "eligible": true | false, // Is the domain already powered? "powerStatus": "active" | "inactive", // Current cnameTarget set on the Dashboard "cnameTarget": URL | "", // Current cnameTarget value for the domain "applicationUrl": URL | "", //Paths with granted access to the root of the Application URL "powerRootPathAccess": [] | ["/path1", "/path2",..., "pathN"] } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | ------------------------------------- | ------ | | Domain provided couldn't be resolved. | 400 | *** ### Power a new domain ```BASH theme={null} POST https://api.goentri.com/power Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "www.example.com", "applicationUrl": "my.applicationurl.com" } ``` Root Path Access is configured once in your application settings (Dashboard or Portal) and applied automatically to every powered domain. See [Root Path Access](/power/root-path-access). #### Successful response (200 status) ```JSON JSON theme={null} { "message": "Success." } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | ------------------------------------------------------ | ------ | | This domain has been already powered. | 502 | | Please review Power eligibility status of this domain. | 502 | | Please complete step 1. | 502 | | Internal Server Error. | 502 | *** ### Update an already powered domain ``` PUT https://api.goentri.com/power Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "www.example.com", "applicationUrl": "my.applicationurl.com" } ``` #### Successful response (200 status) ```JSON JSON theme={null} { "message": "Success." } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | --------------------------------------------------------- | ------ | | `{detailed error message when powering again the domain`} | 502 | *** ### Remove a powered domain ```BASH theme={null} DELETE https://api.goentri.com/power Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "www.example.com" } ``` #### Successful response (200 status) ```JSON JSON theme={null} { "message": "Success." } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | ---------------------------------------------------------------------- | ------ | | Domain not valid | 502 | | detailed error message when deleting the SSL certificate of the domain | 502 | ## Appended Headers on Incoming Traffic As part of the SSL provisioning process, all incoming traffic will have specific headers appended to each request. These headers provide additional context about the original request, including information about the host and IP address. The following headers will be added automatically to every incoming request: * **X-Forwarded-Host**: This header contains the original `Host` value of the incoming request before any proxy or load balancer has modified it. It helps in identifying the initial destination for the request. * **x-entri-forwarded-host**: Similar to `X-Forwarded-Host`, this is a custom header used by Entri to track the original host at the time the request was forwarded through Entri’s system. * **X-Forwarded-IP** or **X-Forwarded-For**: These headers contain the original IP address of the client making the request. It is useful for understanding where the request originated from, even if it passed through proxies or load balancers. * **X-Entri-Auth**: The X-Entri-Auth header is a configurable authentication token that you can set in the Entri Dashboard. Once enabled, it is automatically included in every outgoing request, allowing your system to verify that the request originated from Entri. The token can be rotated at any time to align with your internal security policies. These headers ensure that downstream services can access critical metadata about the request, facilitating better logging, security, and handling of the requests in your system. # Power Events Source: https://developers.entri.com/power/events Webhook events and the power_status field. This page documents the webhook events and payload fields relevant to Entri Power. For the payload envelope and signature verification, see [Webhooks Overview](/webhooks-overview). ## Webhook event types Entri Power augments the standard flow webhook payload with the fields below; it does not define its own event types. See [Webhooks Overview](/webhooks-overview) for the event envelope. ## Status fields * **`power_status`**: Only relevant when using Entri Power. Describes the status of the advanced features configuration. Possible values: * `"pending"`: Setup of advanced features is pending. * `"success"`: Advanced features setup was successful. * `"failed"`: Setup of advanced features failed. * `"exempt"`: Entri Power does not apply to the current flow. ## Payload fields * **`cname_target`**: Only applicable for Entri Power and Secure setups. The CNAME target used for DNS configuration (e.g. `"my.saascompany.com"`). The base flow webhook payload's `data.records_propagated` entries may include the following Power-only fields: * **`applicationUrl`**: (optional, Entri Power only) The application URL tied to the propagated DNS record (e.g. `"www.example.com"`). * **`powerRootPathAccess`**: (optional, Entri Power only) An array of paths accessible through Entri Power. Example: ```json theme={null} ["path1", "path2", "pathN"] ``` # Entri Power Overview Source: https://developers.entri.com/power/overview Root-domain hosting — what Entri Power is and when to use it. Entri Power's REST endpoints (`/power/*`) are served by the **Connect API gateway** at `https://api.goentri.com`, not by a separate Power host. There is no public Power-specific API host. Users care about a fully customizable product experience. That's where custom domains come in. Domain-connected users become fully integrated with your product, resulting in better retention, and a better customer experience for their end users. ## About Entri Power (FAQ) ### How does Entri Power work? Entri uses a reverse proxy with SSL termination. To illustrate the flow of data: 1. A client (such as a web browser) makes a request to your customer's domain. 2. That request goes through Entri's reverse proxy server and is passed along to your service. 3. Your service returns a response through the reverse proxy, which is then passed back to the client and renders your `applicationUrl` ### Why should you trust Entri as a reverse proxy? Entri Power uses Amazon Web Services with [multi-region architecture](https://aws.amazon.com/solutions/implementations/multi-region-application-architecture/) to avoid single points of failure and minimize latency. We also leverage advanced distributed denial of service (DDOS) protection. You can view Entri's uptime history on our [status page](https://status.entri.com/). ### What certificate authority does Entri use? Entri is proud to use [Let's Encrypt](https://letsencrypt.org/) SSL certificates. ### How long on average does it take for an SSL certificate to go live? On average, between 3-7 seconds. ### Can I use Entri Power with Cloudflare? Yes you can. ### How many times can I provision the same domain? You can re-power the same domain a maximum amount of 5 times during a 24 hours period. # Root Path Access Source: https://developers.entri.com/power/root-path-access Serving content at the root path of a Power-connected domain. ## Enabling Root Path Access (RPA) for static files Entri Power provides a method for granting access to the root of the Application URL for specific paths. This feature is particularly beneficial for applications that need to share static files like JS, CSS, or image files among their customers. By utilizing the RPA functionality, the URLs of these files are automatically excluded from being rewritten. ### Configuring RPA in the dashboard You configure Root Path Access directly on your application, so Entri stores it once and applies it to every domain you power. The same field is available in two places, and both read from and write to the same stored value, so a change in one is immediately reflected in the other: * **Dashboard** — Application Settings, in the **Entri Power/Secure** section. * **Portal** — App Settings, in the **Power** section. Enter one path per line in the **Root Path Access** text area and save. A few notes on behavior: * If a value is already stored, it is pre-populated when the page loads. * Clearing the field and saving removes the stored value. * Whitespace-only values are rejected with an inline error. # Provider Limitations Source: https://developers.entri.com/provider-limitations Known constraints and behaviors by DNS provider that may affect automated configuration Some DNS providers enforce restrictions through their APIs, security policies, or implementations that limit what Entri can configure automatically. Understanding these limitations helps you anticipate issues and plan accordingly. These restrictions originate from DNS providers themselves, not from Entri. They are enforced through provider systems and APIs. ## Common Limitation Types Before diving into specific providers, here are the categories of limitations you may encounter: | Type | Description | Impact | | ----------------------- | ---------------------------------------------------------- | ------------------------------- | | **MFA/Security** | Passkeys, security keys, or device-based MFA not supported | User falls back to manual setup | | **Record Restrictions** | TXT >255 chars, wildcards, or CNAME at root not supported | Specific record types fail | | **API Gaps** | No automated replacement of existing records | Manual deletion required first | | **Login Methods** | Social login (Google, Apple) not supported | Credential login required | ## Provider-Specific Limitations ### Amazon Route 53 * No separate hosted zones for subdomains * Passkey/security key login not supported * MFA setup is mandatory ### ArubaIT * DNS changes take up to 30 minutes to propagate * Records cannot be updated during propagation window ### Cloudflare * **Proxy mode**: When enabled via Entri, applies to ALL records created through that flow (not individually configurable) ### CrazyDomains * Email verification may be required after extended inactivity * Premium DNS required for TXT records ### DreamHost * Advanced domain protection plans require email verification on every login * Some services auto-create DNS records that cannot be removed, even if they conflict ### Dynadot * Accounts may lock during repeated login attempts * Repeated invalid credentials can trigger temporary lockouts ### Gandi * Account verification may be required * Some domains use legacy nameservers that don't support advanced DNS automation ### GoDaddy * **TTL values**: GoDaddy uses a fixed TTL of X. If you need a custom TTL, please contact Entri support. * **Conflict limit**: Only allows 3 flows with conflicting records. After that, triggers manual setup * **Parked A records**: Tied to GoDaddy's free products. Entri removes them automatically if they conflict with your records ### GreenGeeks * Domains pointing to expired or missing hosting cannot be managed * DNS operations fail if hosting is inactive ### Hosting.com * Some domains require admin-level login access ### InMotion * Invalid password errors are only surfaced after MFA submission ### O2switch * Requires a cPanel password to be set for new hosting domains ### One.com * **MFA**: Uses a companion mobile app for two-factor authentication. This MFA method is not currently supported by Entri * Users with app-based MFA will be redirected to manual setup ### Papaki * Nameservers must be manually configured before DNS automation is possible ### Registro.br * **Domain in transition**: When enabling advanced DNS for the first time, Registro.br may take up to 5 minutes to transition the domain. During this window, DNS operations will fail with a `RegistroDomainInTransition` error. Retry after 5 minutes. ### Shopify * Passkey, social, and biometric logins not supported * TXT records longer than 255 characters not supported * Record values must be entered as FQDNs * multiple A records at @ host is not supported ### Simply * Explicit permission from the account owner is required ### Spaceship * **Domain location**: Domain must be in "Web Hosting" section, not "SellerHub". Domains listed under SellerHub cannot be configured automatically * USB security key login not supported ### Squarespace * **No automated replacement**: If a record already exists, Entri redirects to manual setup * **Workaround**: User must manually delete conflicting record first, then re-run Entri ### NetworkSolutions.com, Bluehost.com, Hostgator.com * Password reset required after long inactivity * Migrated accounts may not be able to authenticate ### Record-Specific Limitations #### TXT Records >255 Characters Not supported by: * Shopify * WordPress.com * Hover **Workaround**: Split the record if your application supports it, or use manual setup. #### Wildcard Records Not supported by: * Strato * Wix * OVH * LocaWeb * OpenSRS #### CNAME at Root (@) Most providers don't support CNAME records at the root domain due to DNS specification constraints. Some providers work around this using CNAME flattening, ALIAS, or ANAME records, but support varies and may have restrictions. See [DNS Concepts](/dns-concepts#cname-at-root-the-technical-limitation) for a detailed explanation. Use `checkDomain` to detect support programmatically: ```javascript theme={null} const result = await entri.checkDomain("example.com", config); if (result.cnameFlattening) { // Provider supports CNAME at root } ``` **Workarounds for providers without support:** 1. Use A records pointing to your server's IP 2. Use [Entri Secure](/secure/overview) with `secureRootDomain: true` 3. Redirect root to www subdomain (`wwwRedirect: true`) ## Security-Related Limitations ### Login Method Compatibility Some providers don't support all authentication methods. When a user attempts to log in with an unsupported method (such as passkeys, social login, or hardware security keys), Entri automatically redirects them to manual setup with clear instructions. If your users report being unexpectedly redirected to manual setup, verify they're using standard credential-based login with app-based or SMS MFA. ### Supported MFA Methods Multi-factor authentication support varies by provider: **Amazon Route 53** * ✅ Authenticator apps (Google Authenticator, Authy, Microsoft Authenticator) * ✅ Email verification codes * ❌ Other MFA methods trigger manual setup **All Other Providers** * ✅ SMS text messages * ✅ Authenticator apps * ✅ Email verification codes If a user has hardware security keys (YubiKey), passkeys, or device-based MFA enabled, they will be redirected to manual setup. Consider documenting this in your user-facing help content. ### Domain Protection Features The following can block automated DNS changes: * **NameSilo**: Domain Defender * **Reg123**: Domain Protection setting Users with these features enabled will be redirected to manual setup. ## Troubleshooting If a user encounters issues during automated setup: 1. Run `entri.checkDomain(domain, config)` to verify provider detection and capabilities 2. Check [provider health status](/webhooks-overview#provider-health) 3. Verify the user isn't using an unsupported login method 4. Check if domain protection features are enabled 5. For TXT records, verify length is under 255 characters 6. If all else fails, use `forceManualSetup: true` and provide clear instructions ## Need Help? If you encounter issues not listed here, contact Entri support with: * Domain name * Provider name * Error message or screenshot * DNS records you're trying to configure # Responsible Disclosure Policy Source: https://developers.entri.com/responsible-disclosure-policy How to report a security vulnerability to Entri, plus our disclosure guidelines and exclusions. Data security is a top priority for Entri, and Entri believes that working with skilled security researchers can identify weaknesses in any technology. If you believe you’ve found a security vulnerability in Entri’s service, please notify us; we will work with you to resolve the issue promptly. ## Disclosure Policy If you believe you’ve discovered a potential vulnerability, please let us know by emailing us at security (at) goentri.com We will acknowledge your email promptly. Provide us with a reasonable amount of time to resolve the issue before disclosing it to the public or a third party. Make a good faith effort to avoid violating privacy, destroying data, or interrupting or degrading the Entri service. Please only interact with accounts you own or for which you have explicit permission from the account holder. ## Exclusions While researching, we’d like you to refrain from: * Distributed Denial of Service (DDoS) * Spamming * Social engineering or phishing of Entri employees or contractors * Any attacks against Entri’s physical property or data centers Thank you for helping to keep Entri and our users safe! ## Changes We may revise these guidelines from time to time. The most current version of the guidelines will be available at [https://developers.entri.com/responsible-disclosure-policy](/responsible-disclosure-policy). Entri is always open to feedback, questions, and suggestions. If you would like to talk to us, please feel free to email us at [security@goentri.com](mailto:security@goentri.com). # Secure Configuration Source: https://developers.entri.com/secure/configuration showEntri() config keys for Secure: ssl on dnsRecords, secureRootDomain, applicationUrl. ## Configure your Entri account Before you can provision SSL certificates on Entri, you'll need to provide some basic information. Log into the [Entri Dashboard](https://dashboard.entri.com/), navigate to the SSL section, and enter the following information: * The `applicationUrl`, which is the URL of the application that responds to requests coming from your clients' URLs. This is also commonly referred to as an origin server. * Your company’s `cname_target`. This is the CNAME record that your customers need. It needs to be pointed to `ssl.goentri.com` and will be the target domain for your clients’ requests, providing a layer of security and encryption. For example, if you run `saascompany.com`. You would first create a CNAME record: ```json json theme={null} { "type": "CNAME", "host": "domains", "value": "ssl.goentri.com", "ttl": 300, } ``` You're now ready to provision SSL certificates for your customers. ## Provision a certificate After you've configured your account for SSL, there are two methods of provisioning an SSL certificate for your customer's domain: utilizing the ***Entri modal***, or making a direct API request. ### Provisioning an SSL certificate via the Entri Modal (recommended) Provisioning a certificate for a subdomain is effortless with the ***Entri modal***. Add an extra property, `ssl: true`, to the CNAME record that will be set by Entri (as shown in the configuration object below) and set `value` to be `{CNAME_TARGET}`. `{CNAME_TARGET}` will automatically use the **CNAME target** entered in the dashboard in step 1. ```JSON JSON theme={null} { "type": "CNAME", "host": "www", "value": "{CNAME_TARGET}", // This will match the CNAME target defined in the Entri dashboard in step 1 "ttl": 300, "ssl": true, } ``` We suggest this method because it guarantees that your user has properly added the required `cname_target` record. Without this CNAME record added to your customer's DNS, Entri cannot provision an SSL certificate. Additionally, if the user has a conflicting CAA record, Entri will automatically fix it during the DNS setup process. For more information about configuring the ***Entri modal***, see: [Create the configuration object](/connect/configuration#entrishowentriconfig). ### SSL certificates for the root domain via the Entri Modal You can also create an SSL certificate for the root domain using Entri. It will require you to add an extra dnsRecord to the configuration, and Entri will do all the rest for you. This feature will create 2 new A records with Entri's IPs on the root domain's dns configuration and will use Entri as proxy to encrypt and forward the traffic to your application. ```JSON JSON theme={null} { type: "A", host: "@", value: "{ENTRI_SERVERS}", ttl: 300, ssl: true, applicationUrl: "my.applicationurl.com", // [Optional] overrides the pre-configured applicationUrl redirectTo: "some.domain.com" //[Optional] Allows you to create a custom redirect policy from the secured domain to any other domain or subdomain. } ``` You should only use the `applicationUrl` key, whenever you need to override the applicationUrl configured on your Customer Dashboard. ## Get notified when the SSL certificate is provisioned You can use [Entri's Webhook functionality](/webhooks-overview) to listen for when an SSL certificate has been provisioned on a domain. Check for `"secure_status": "success"` in the webhook payload. ## Appended Headers on Incoming Traffic As part of the SSL provisioning process, all incoming traffic will have specific headers appended to each request. These headers provide additional context about the original request, including information about the host and IP address. The following headers will be added automatically to every incoming request: * **X-Forwarded-Host**: This header contains the original `Host` value of the incoming request before any proxy or load balancer has modified it. It helps in identifying the initial destination for the request. * **x-entri-forwarded-host**: Similar to `X-Forwarded-Host`, this is a custom header used by Entri to track the original host at the time the request was forwarded through Entri’s system. * **X-Forwarded-IP** or **X-Forwarded-For**: These headers contain the original IP address of the client making the request. It is useful for understanding where the request originated from, even if it passed through proxies or load balancers. * **X-Entri-Auth**: The X-Entri-Auth header is a configurable authentication token that you can set in the Entri Dashboard. Once enabled, it is automatically included in every outgoing request, allowing your system to verify that the request originated from Entri. The token can be rotated at any time to align with your internal security policies. These headers ensure that downstream services can access critical metadata about the request, facilitating better logging, security, and handling of the requests in your system. ### Secure root domains This feature allows you to create a valid SSL certificate for the root domain, in a way that it can be used either as a redirect rule to the `www` subdomain or as a way to show the content directly. This feature, in all cases, will create 2 new `A` records with Entri's IPs to the root domain's dns configuration, and will use Entri as proxy to encrypt and forward the traffic to your application. IMPORTANT: This feature requires to have [Entri Secure](/secure/overview) or [Entri Power](/power/overview) enabled on your account. #### Basic usage (wwwRedirect only) This configuration may be used for securing the root domain (e.g. `https://mydomain.com`) and redirect it to `www` (eg. `https://www.mydomain.com`) ```JSON JSON theme={null} { applicationId: "12345", token: mySavedToken, secureRootDomain: true, wwwRedirect:true, dnsRecords: [...] } ``` #### Advanced usage (display content) This configuration may be used for securing the root domain (e.g. `https://mydomain.com`) and show the content directly on the root domain level, without redirections. ```JSON JSON theme={null} { applicationId: "12345", token: mySavedToken, secureRootDomain: true, wwwRedirect:true, dnsRecords: [ { type: "A", host: "@", value: "{ENTRI_SERVERS}", ttl: 300, ssl: true, applicationUrl: "my.applicationurl.com" // [Optional] overrides the pre-configured applicationUrl }, ... //Other records ] } ``` # Secure Endpoints Source: https://developers.entri.com/secure/endpoints REST endpoints for Entri Secure. Secure endpoints are served by the Connect API gateway, not by a separate Secure host. ## Secure API - SSL certificates ### List secured domains Partners can retrieve all domains currently **Secured** under a given `applicationId` to audit configuration, build admin UIs, and perform maintenance. This endpoint returns a paginated list of all Secure-managed domains for the authenticated application. ```txt theme={null} GET https://api.goentri.com/ssl Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Headers | Header | Required? | Description | | ------------- | --------- | -------------------------------------------- | | Authorization | Yes | Bearer token (JWT) for authentication. | | applicationId | Yes | Your Application ID as set in the dashboard. | #### Query string parameters | Name | Type | Required? | Default | Description | | ------ | ------- | --------- | ------- | ------------------------------------------------ | | page | integer | No | 0 | Zero-based page index (0 = first page). | | size | integer | No | 50 | Max number of items to return (range: 1–200). | | status | string | No | — | Filter by secure status: `active` or `inactive`. | > **Note:** `status` is optional. If omitted, the endpoint returns **all** secured domains for the application. #### Sample response ```json theme={null} { "items": [ { "domain": "www.example.com", "applicationUrl": "my.applicationurl.com", "secureStatus": "active" }, { "domain": "blog.example.com", "applicationUrl": "my.applicationurl.com", "secureStatus": "inactive" } ], "total": 2, "page": 0, "limit": 50, "count": 2, "pages": 1, "nextPage": null, "prevPage": null } ``` #### Field definitions * **domain** (`string`): Fully qualified domain or subdomain configured via Secure. * **applicationUrl** (`string`): Current upstream URL for this domain. * **secureStatus** (`string`): `active` or `inactive`. * **total** (`integer`): Total number of secured domains matching the filter. * **page** (`integer`): Current zero-based page index. * **limit** (`integer`): Maximum number of items requested per page. * **count** (`integer`): Number of items returned. * **pages** (`integer`): Total number of pages available. * **nextPage** (`integer | null`): Next page index, or null if not applicable. * **prevPage** (`integer | null`): Previous page index, or null if not applicable. #### Error responses | HTTP | Example Body | When | | ---- | -------------------------------------------- | ----------------------------------------- | | 400 | `{ "message": "Invalid 'limit' parameter" }` | Validation errors on query parameters. | | 401 | `{ "message": "Unauthorized" }` | Missing/invalid JWT or `applicationId`. | | 403 | `{ "message": "Forbidden" }` | Authenticated but forbidden for this app. | | 500 | `{ "message": "Internal Server Error" }` | Unexpected server-side error. | #### Rate limits This endpoint follows the same global rate limits as other Secure API endpoints. #### Example cURL ```bash theme={null} curl -X GET "https://api.goentri.com/ssl?status=active&size=10&page=0 \ -H "Authorization: Bearer " \ -H "applicationId: " ``` ### Check the domain's eligibility ```bash theme={null} GET https://api.goentri.com/ssl?domain=www.example.com&rootDomain=false Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Parameters description | Parameter | Required? | Default | Description | | ---------- | --------- | ------- | --------------------------------------------------------------------------------- | | domain | Yes | N/A | The domain name to check | | rootDomain | No | false | true if checking the root domain's eligibilityfalse if checking for the subdomain | #### Successful response (200 status) ```JSON JSON theme={null} { // Is the domain CNAMEed to the cname_target of the application? "eligible": true | false, // Is SSL provisioned? "sslStatus": "active" | "inactive", // Current cnameTarget set on the Dashboard "cnameTarget": URL | "", // Current cnameTarget value for the domain "applicationUrl": URL | "" } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | ------------------------------------- | ------ | | Domain provided couldn't be resolved. | 400 | *** ### Provision an SSL certificate ``` POST https://api.goentri.com/ssl Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "www.example.com", "applicationUrl": "my.applicationurl.com", /*Optional*/ "redirectTo": "shops.example.com" /*Optional*/ } ``` #### Parameters description | Parameter | Required? | Default | Description | | -------------- | --------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------- | | domain | Yes | N/A | The domain name to check | | applicationUrl | No | Configured on Customer's Dashboard. | Overrides the pre-configured value set on the Dashboard. | | redirectTo | No | N/A | Allows you to create a custom redirect policy from the secured domain to any other domain or subdomain. | #### Successful response (200 status) ```JSON JSON theme={null} { "message": "Success." } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | ----------------------------------------------------- | ------ | | There is already a valid certificate for this domain. | 502 | | Please review SSL eligibility status of this domain. | 502 | | Please complete step 1. | 502 | | Internal Server Error. | 502 | | ApplicationUrl is not reachable | 502 | *** ### Renew an SSL certificate ```BASH theme={null} PUT https://api.goentri.com/ssl Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "www.example.com" } ``` #### Successful response (200 status) ```json json theme={null} { "message": "Success." } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | --------------------------------------------------- | ------ | | `{detailed error message when renewing the domain}` | 502 | *** ### Deprovision an SSL certificate ```BASH theme={null} DELETE https://api.goentri.com/ssl Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "domain": "www.example.com" } ``` #### Successful response (200 status) ```JSON JSON theme={null} { "message": "Success." } ``` #### Possible error responses Errors are returned as a `{message: 'string'}` object. | Error message | Status | | -------------------------------------------------------------------------- | ------ | | Domain not valid | 502 | | `{detailed error message when deleting the SSL certificate of the domain}` | 502 | # Secure Events Source: https://developers.entri.com/secure/events Webhook events and the secure_status field. This page documents the webhook events and payload fields relevant to Entri Secure. For the payload envelope and signature verification, see [Webhooks Overview](/webhooks-overview). ## Webhook event types Entri Secure augments the standard flow webhook payload with the fields below; it does not define its own event types. See [Webhooks Overview](/webhooks-overview) for the event envelope. ## Status fields * **`secure_status`**: Only relevant when using Entri Secure. Describes the status of SSL security configuration. Possible values: * `"pending"`: SSL configuration is pending. * `"success"`: SSL setup was successful. * `"failed"`: SSL setup failed. * `"exempt"`: Entri Secure does not apply to the current flow. ## Payload fields * **`cname_target`**: Only applicable for Entri Power and Secure setups. The CNAME target used for DNS configuration (e.g. `"my.saascompany.com"`). # Entri Secure Overview Source: https://developers.entri.com/secure/overview Automated SSL provisioning — what Entri Secure is and when to use it. Entri Secure's REST endpoints (`/ssl/*`) are served by the **Connect API gateway** at `https://api.goentri.com`, not by a separate Secure host. Under the hood, Secure runs on an SNS → SQS → Lambda pipeline and does not expose its own public API. Entri has the ability to automatically provision SSL certificates. Your happy customers will be able to secure custom encrypted domains for your application instantly with no extra work required by your engineering team. ## About Entri Secure SSL certificates (FAQ) ### How does Entri Secure's SSL configuration work? Entri uses a reverse proxy with SSL termination. To illustrate the flow of data: 1. A client (such as a web browser) makes a request to your customer's domain. 2. That request goes through Entri's reverse proxy server and is passed along to your service. 3. Your service returns a response through the reverse proxy, which is then passed back to the client. ### Why should you trust Entri Secure as a reverse proxy? Entri Secure uses Amazon Web Services with [multi-region architecture](https://aws.amazon.com/solutions/implementations/multi-region-application-architecture/) to avoid single points of failure and minimize latency. We also leverage advanced distributed denial of service (DDOS) protection. You can view Entri's uptime history on our [status page](https://status.entri.com/). ### What certificate authority does Entri Secure use? Entri is proud to use [Let's Encrypt](https://letsencrypt.org/) SSL certificates. ### How long on average does it take for the certificate to go live? On average, between 3-7 seconds. ### Can I use Entri Secure with Cloudflare? Yes you can. ### How many times can I provision the same domain? You can re-provision for the same domain a maximum amount of 5 times during a 24 hours period. # Sell Events Source: https://developers.entri.com/sell/events Entri Sell webhook events (domain.purchased, purchase.error, purchase.confirmation.expired) and browser callback events (onSuccess, onEntriClose). This page documents the webhook events and payload fields relevant to Entri Sell. For the payload envelope and signature verification, see [Webhooks Overview](/webhooks-overview). ## Webhook event types * `"domain.purchased"`: Indicates that the domain *order* creation has been completed. * `"domain.transfer.in.initiated"`: Only applicable for **Entri Sell Enterprise**. An inbound domain transfer has been initiated by the user. * `"domain.transfer.in.started"`: Only applicable for **Entri Sell Enterprise**. The inbound domain transfer has been accepted and is now in progress at the registry level. * `"domain.transfer.in.ack"`: Only applicable for **Entri Sell Enterprise**. The inbound domain transfer has been acknowledged by the registry. * `"domain.transfer.in.failed"`: Only applicable for **Entri Sell Enterprise**. The inbound domain transfer has failed. * `"domain.transfer.out.initiated"`: Only applicable for **Entri Sell Enterprise**. An outbound domain transfer away from Entri has been initiated. * `"purchase.error"`: Indicates an error during the domain purchase process. * `"purchase.confirmation.expired"`: The purchase confirmation has expired (abandoned flow). ## Payload fields * **`purchased_domains`**: An array of domains that were purchased during the transaction. This field is only relevant for Entri Sell. Example: ```json theme={null} ["domain1.com", "domain2.com", "domainN.com"] ``` * **`free_domain`**: Indicates whether the domain was obtained through a free domain flow. Possible values: * `true`: The domain was free. * `false`: The domain was not free. ## Browser callback events The Entri Sell modal emits browser events you can listen for to react when a purchase completes or the modal is closed. Each is a `window` event whose `event.detail` carries the payload described below. ### `onSuccess` Callback Event Fired when the user reaches the **Congratulations** screen after a successful purchase. Use it to capture a definitive success signal and the `jobId` that identifies the flow. Both the IONOS and Squarespace Sell flows fire `onSuccess`. #### Sample usage: ```JavaScript JavaScript theme={null} function handleOnSuccess(event) { console.log('onSuccess', event.detail); } window.addEventListener('onSuccess', handleOnSuccess, false); ``` #### Sample `event.detail` object response: ```JSON JSON theme={null} { "domain": "example.com", "success": true, "setupType": "purchase", "provider": "squarespace", "freeDomain": false, "jobId": "2132b20f-e522-42fc-9604-5a53b1f13a4b" } ``` #### Event details object description | Name | Type | Description | | ---------- | ------- | --------------------------------------------------------------------------------------------------------- | | domain | string | The purchased domain. When multiple domains are purchased, they are returned as a comma-separated string. | | success | boolean | Always `true` for this event. | | setupType | string | Always `purchase` for Sell flows. | | provider | string | The Sell registrar handling the purchase (e.g. `squarespace`). | | freeDomain | boolean | `true` when the purchase used an Entri Sell free-domain flow; otherwise `false`. | | jobId | string | The unique identifier for the Sell flow. It is also included in the related webhooks. | The `onSuccess` event is the recommended source to capture the `jobId` for subsequent server-side operations (e.g., retrieving the last webhook). ### `onEntriClose` Callback Event Fired as soon as the modal is closed, with the latest status of the flow. #### Sample usage: ```JavaScript JavaScript theme={null} function handleOnEntriClose(event) { console.log('onEntriClose', event.detail); } window.addEventListener('onEntriClose', handleOnEntriClose, false); ``` #### Sample `event.detail` object response: ```JSON JSON theme={null} { "domain": "example.com", "success": true, "lastStatus": "FINISHED_SUCCESSFULLY", "provider": "squarespace", "setupType": "purchase" } ``` #### Event details object description | Name | Type | Description | | ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | domain | string | The domain the user entered. May be an empty string if the user closed the modal before selecting a domain. | | success | boolean | `true` if the purchase completed before the modal closed; otherwise `false`. | | lastStatus | string | `FINISHED_SUCCESSFULLY` when the purchase completed successfully. Omitted when the modal is closed without completing a purchase. | | provider | string | The Sell registrar handling the purchase (e.g. `squarespace`). | | setupType | string | Always `purchase` for Sell flows. | The payload depends on the registrar and the outcome. The **IONOS** close event includes only `domain` and `success`. When the modal is closed without a completed purchase, `success` is `false` and `lastStatus` is omitted. # Sell Modal Integration Source: https://developers.entri.com/sell/modal-integration Unified Flow — embedded domain purchase modal via showEntri/purchaseDomain, including Sell-specific config keys. ## Integration Guide ### **Step 1: Configure Entri** To set up Sell, first follow the [**Getting Started guide**](/getting-started). Be sure to use [**dynamic configuration variables**](/connect/configuration#dynamic-configuration-variables) if your DNS records change based on the domain purchased. ### **Step 2: Select your Sell registrar partner** To start selling domains, you’ll need to select your Sell registrar partner. By default, no partner is selected in your account. Each partner will provide a different checkout experience, and a different set of functionality (top level domains, languages, regional support etc.). To learn more about each partner, please visit the Sell Registrars page or the Entri dashboard. After logging into the Entri Dashboard, visit the Application Settings page and the Sell Registrars tab. On this tab you can preview each Sell experience, learn more about the functionality offered by each partner, and select your registrar for each of your `ApplicationIDs`. Select your Sell registrar partner ### **Step 3: Define your return URLs (optional)** By default, Entri Sell redirects the user back to your platform if they complete or exit the purchase flow, on the same page that they were referred from. But, if you’d like to specify a different return URL, you can define this: * **`successCallbackUrl`**: The URL to which the user will be redirected after a successful domain purchase. * **`exitCallbackUrl`**: The URL to which the user will be redirected if they select Close (X) before completing the purchase flow. See more info at [**Basic Configuration**](#basic-configuration). ### **Step 4: Call purchaseDomain()** Call the `purchaseDomain` method with your configuration object to open the Entri Purchase Domain Flow. ```html theme={null} ``` ### **Configuration object specifications** More information about the properties within the object that you’ll pass to the `entri.purchaseDomain` method. #### **Basic Configuration** * **`applicationId`** (string): A unique identifier for your application, available in the Entri dashboard. **Example:** **`"12345"`** * **`token`** (string): A JWT token used for authentication and authorization. **Example:** **`"JWT_TOKEN"`** * **`successCallbackUrl`** (string) **(optional)**: The URL to which the user will be redirected after a successful domain purchase. The purchased domain will be added as a **`domain`** query string parameter to the success callback URL. **Example:** **`"https://myapplication.com/success"`** (redirects to **`https://myapplication.com/success?domain=mydomain.com`**) * **`exitCallbackUrl`** (string) **(optional)**: The URL to which the user will be redirected if they select Close (X) before completing the purchase flow. **Example:** **`"https://myapplication.com/exit"`** * **`disableConnect`** (boolean) **(optional)**: When `true`, the flow only performs the domain purchase — no DNS records are configured for the purchased domain. Useful when your app sets up DNS out-of-band. **Default:** `false`. * **`apiLink`** (string) **(optional)**: Overrides the base API endpoint the modal calls. Most integrations should leave this unset; use it only when directed by your Entri account manager (for example, to target a regional endpoint). * **`whiteLabel.sell.flowTarget`** (string) **(optional)**: The target location where the purchase flow window will be displayed. If `_blank` is specified, the flow will open in a new browser tab. **Default:** `_parent`. #### **DNS Records** * **`dnsRecords`** (array of objects): An array containing DNS record objects. These records will applied after a successful domain purchase. * **Example:** ```json theme={null} [ { "type": "A", "host": "@", "value": "93.184.216.34", "ttl": 300 }, { "type": "A", "host": "aipv4", "value": "93.184.216.34", "ttl": 300 } ] ``` ### Webhook Notifications When your users go through the Sell flow, Entri will send you webhook notifications to update you on their progress and the status of their purchase. To learn more about the configuration and webhook notifications, please visit our [webhook documentation page](/webhooks-overview). #### **Other Settings** * **`freeDomain`** (boolean): Indicates whether the domain price should be set to \$0. Should only be used on paid users at the customer’s platform. **Example:** **`true`** * Supported by **both IONOS and Squarespace**, enabled the same way. IONOS offers free domains across its full TLD catalog; Squarespace supports a limited set of TLDs. Reach out to your account manager for the current eligible TLD list on Squarespace and for access. ```js JavaScript theme={null} fetch("https://api.goentri.com/token", { method: "POST", body: JSON.stringify({ // These values come from the Entri dashboard applicationId: "12345", secret: "12345-67890", freeDomain: true, }), }) .then((response) => response.json()) .then((data) => { console.log("Success:", data); // { "auth_token": "exampletoken..." } // Save the token in a variable or state manager for later use }); ``` * **`locale`** (string): The language/locale to be used in the domain purchase flow. **Example:** **`"en"`** #### **Advanced Security Settings for `freeDomain`** For additional security, you can explicitly set `freeDomain:false` or `freeDomain:true`when you create the JWT. This way, the user gets explicit authorization for running the free domain flow\.If the client tries to use a config that doesn’t match the one hashed in the JWT, it will return an error. Here’s an example of how to use this when generating the JWT: | **Property** | **Type** | **Required** | **Default** | **Description** | | ---------------- | -------- | ------------ | ----------- | ---------------------------------------------------------------------------------------- | | **`freeDomain`** | boolean | No | N/A | When set to **`true`**, it will only allow Entri to run under the **`freeDomain`** flow. | ```js JavaScript theme={null} fetch("https://api.goentri.com/token", { method: "POST", body: JSON.stringify({ // These values come from the Entri dashboard applicationId: "12345", secret: "12345-67890", freeDomain: true, }), }) .then((response) => response.json()) .then((data) => { console.log("Success:", data); // { "auth_token": "exampletoken..." } // Save the token in a variable or state manager for later use }); ``` #### **Mail Upsell** Mail Upsell is currently only available when using **IONOS** as the domain registrar. By default, Entri adds an extra step to the domain purchase flow. This step offers users the opportunity to purchase mail inboxes as an upsell. A mailbox is optional, meaning users can manually skip the mail offering if they are not interested. This property enhances the domain purchase experience by allowing you to promote additional services seamlessly, providing more value to users while keeping the flow non-intrusive. You can turn this feature off by setting `disableMailUpsell: true` in your configuration. Example Configuration: ```json theme={null} { "applicationId": "exampleapp", "token": "JWT_TOKEN", "disableMailUpsell": true, "successCallbackUrl": "https://[your_success_callback_url].com", "exitCallbackUrl": "https://[your_exit_callback_url].com", "dnsRecords": [ { "type": "A", "host": "@", "value": "93.184.216.34", "ttl": 300 } ], "whiteLabel": { "logo": "https://cdn.yourpartner.com/logo.svg", "theme": { "fg": "#ffffff", "bg": "#012939", "fgSecondary": "#ffffff", "bgSecondary": "#012939" } } } ``` #### Post purchase asynchronous DNS configurations Entri offers the ability for you to perform DNS configuration updates after the Sell flow has taken place. To learn more about how this feature can be used, please refer to the [Asynchronous DNS configurations](/sell/sdk#asynchronous-dns-configurations-entri-sell-only) section. ## Check Domain Availability This endpoint checks whether a domain is available for registration before initiating the **Entri Sell** flow. It returns the availability status along with a reason why the domain is not available, helping you determine whether to proceed with the purchase process. ### Check Domain Availability as HTTP endpoint ```txt theme={null} GET https://api.goentri.com/checkdomainavailability Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Headers | Header | Required? | Description | | ------------- | --------- | -------------------------------------------- | | Authorization | Yes | Bearer token (JWT) for authentication. | | applicationId | Yes | Your Application ID as set in the dashboard. | #### Query Parameters | Parameter | Type | Required? | Description | | --------- | ------ | --------- | ---------------------------------------------------------- | | domain | string | Yes | Fully qualified domain name to check (e.g. `example.com`). | #### Sample Response ```json theme={null} { "domain": "example.com", "available": true, "reason": "AVAILABLE", // or "UNSUPPORTED_TLD", "ERROR", "UNAVAILABLE" "checkedAt": "2025-05-15T17:20:00Z" } ``` ## Sharing Links API — Entri Sell ``` POST https://api.goentri.com/sharing/sell Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Request body ```JSON JSON theme={null} { "applicationId":"yourApplicationID", "config":{ "prefilledDomain": "mydomain.com", "dnsRecords":[ { "type":"CNAME", "host":"www", "value":"test.com", "ttl":300 } ] } } //All the rest of possible configuration options, see https://developers.entri.com/connect/configuration for more details. ``` #### Successful response (200 status) ```JSON JSON theme={null} { // If a custom Sharing Link domain is configured, "link" will reflect that domain. // See: https://developers.entri.com/connect/shared-links#sharing-link-custom-domain "link": "https://app.goentri.com/share/dd339cf05f2646539e79251f8e62f0c5", "job_id": "9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de" } ``` # Entri Sell Overview Source: https://developers.entri.com/sell/overview What Entri Sell is and how the domain purchase modal works. ## Overview With Entri Sell, your users can buy and connect a domain name to your application effortlessly, right within the Entri modal. After purchase, Entri automatically connects their domain to your service, applying the records you define on the **`dnsRecords`** object. You choose a registrar partner — **IONOS**, **Squarespace**, or **GoDaddy** — to power the checkout experience, and Entri handles the connection. The pages below walk you through integrating the modal, comparing registrars, theming the flow to match your brand, and handling post-purchase events. ## Explore the docs * [**Integration Guide**](/sell/modal-integration) — Configure Entri, select your registrar, and call `purchaseDomain()` to open the flow. * [**Sell Registrars**](/sell/white-label#sell-registrars) — Compare the registrar partners and the functionality each one offers: * [IONOS](/sell/white-label#ionos) * [Squarespace](/sell/white-label#squarespace) * [GoDaddy](/sell/white-label#godaddy) * [**White Label & Theming**](/sell/white-label) — Customize colors, partner branding, and custom copy in the purchase flow. * [**Sell SDK**](/sell/sdk) — Method and type reference for the Sell SDK. * [**Sell Events**](/sell/events) — Listen for purchase-flow events emitted by the modal. * [**Frequently Asked Questions**](#frequently-asked-questions) — Registrar, billing, support, and commission questions. *** ## Frequently Asked Questions ### Who is the registrar for the domain? Entri has partnered with IONOS, Squarespace, and GoDaddy, and you can choose between any of these partners as your domain registrar. ### How should I decide which registrar partner to use? Our team can walk you through the checkout experience, TLD coverage, language support, and other functionality offered by each registrar, but this decision should be made based on which partner best supports your business goals and your users. ### How is billing handled? Billing, renewals, and the checkout process are handled by the Sell registrar (IONOS, Squarespace, or GoDaddy) and Entri. ### Who is responsible for customer support relating to the domain purchased? The Sell registrar (IONOS, Squarespace, or GoDaddy) will handle customer support for the domain purchase. If you have any inquiries related to billing, renewals, domain registration, please direct your customers to each respective support team contact. For IONOS support, please visit: **[https://contact.ionos.com/](https://contact.ionos.com/)** For Squarespace support, please reach out to **[support@entri.com](mailto:support@entri.com)** For GoDaddy support, please visit **[https://www.godaddy.com/help](https://www.godaddy.com/help)** ### Can I earn a commission for each domain purchased? Yes! Please contact your Entri account manager to set this up. # Sell SDK Source: https://developers.entri.com/sell/sdk The purchaseDomain() method, configuration options, and error handling for the Entri Sell modal. ## `entri.purchaseDomain(config)` This method launches the [Entri Sell](/sell/overview) modal. The `config` object includes the same set of properties as those specified in [entri.showEntri(config)](/connect/configuration#entrishowentriconfig), plus additional Sell-specific options: For browser callback events (`onSuccess`, `onEntriClose`), see [Sell Events](/sell/events#browser-callback-events). | Property | Type | Required? | Default | Description | | ----------------------------- | ------- | --------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `disableConnect` | boolean | No | false | If true, the flow will only run the domain(s) purchase process, but no specific DNS records will be configured. | | `debugMode` | boolean | No | false | For testing purposes. It skips the actual payment process and takes the user to the congratulations step. | | `successCallbackUrl` | string | No | N/A | The URL where the user will be redirected after a successful domain purchase. | | `exitCallbackUrl` | string | No | N/A | The URL where the user will be redirected if they exit the purchase flow before completing it (e.g. closing the modal). | | `dnsRecords` | array | Yes | N/A | An array of DNS records to configure for the purchased domain. Each record should include `type`, `host`, `value`, and `ttl`. | | `freeDomain` | boolean | No | false | This feature allows you to offer your users their first domain at no cost. Should only be used on paid users on your platform. Please contact your account manager to enable this feature. | | `locale` | string | No | `"en"` | The language/locale used in the domain purchase flow. | | `disableMailUpsell` | boolean | No | false | If set to true, the mail inbox upsell step is disabled and will not appear in the domain purchase flow. This property allows you to prevent the mail inbox upsell step seamlessly, ensuring a streamlined user experience without optional distractions. | | `searchType` | string | No | `"classic"` | (IONOS only) Use "ai" to enable AI-generated domain suggestions instead of the default "classic" search. | | `whiteLabel` | object | No | N/A | Customizes the branding and appearance of the purchase flow. Includes options for setting the logo, theme colors, hiding logos, and customizing text on the initial screen. | | `whiteLabel.sell.flowTarget` | string | No | `_parent` | The target location where the purchase flow window will be displayed. If \_blank is specified, the flow will open in a new browser tab. | | `whiteLabel.sell.partnerLogo` | string | No | N/A | The URL of the partner’s logo, which will be displayed prominently in the cobranded header of the purchase flow. | | `whiteLabel.sell.partnerName` | string | No | N/A | The name of the partner company to display within the purchase flow. | | `whiteLabel.sell.contact` | object | No | N/A | Prefilled user data required for creating the domain. This includes fields like `firstName`, `lastName`, `email`, `phone`, `address`, `zip`, `city`, `state`, `postalCode`, and `country`. | This `purchaseDomain` method includes all the latest theming options, cobranded elements, and features such as `successCallbackUrl` and `exitCallbackUrl`. Make sure to set the appropriate fields to customize the flow according to your needs. ### Possible `purchaseDomain()` Errors When any of the following critical errors occur, a console error will be logged, and the user will only see “Entri is misconfigured. Please contact support for assistance.” in the UI. | Error | Console Message | Reason | | ------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | Failed to Fetch Application Settings | "Failed to fetch application settings" | API call failed (network error, invalid appId, or API down). | | Missing Preferred Registrar | "Entri is misconfigured. Your preferred registrar is missing. Please contact support for assistance." | No `preferred_registrar` found in config or application settings (required for purchase flow). | | Unsupported Registrar | "Preferred registrar is not supported. Please contact support for assistance." | The provided `preferred_registrar` is unsupported (currently supported: `ionos`, `squarespace`). | ## Asynchronous DNS configurations ([Entri Sell](/sell/overview) only) You can make asynchronous DNS configurations after the initial **Entri Sell** flow has finished, with time limits depending on the registrar or plan tier. * For **Squarespace** domains, it remains available for up to **1 year** after the initial domain purchase. * For **IONOS** domains, it remains available for up to **1 year** after the initial domain purchase. For IONOS domains, the 1-year async configuration window only applies to new domains ordered after **December 15, 2025**. This feature allows you to add or update DNS records after the initial setup without requiring users to repeat the full flow. (Not supported on `debugMode=true` flows.) ```bash theme={null} POST https://api.goentri.com/connect/async Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Sample body: ```JSON JSON theme={null} { "applicationId": "clickfunnels", "domain": "domain.com", "subdomain": "www", "wwwRedirect": false, "dnsRecords": [ { "value": "13.248.155.104", "host": "@", "ttl": 300, "type": "A" } ] } ``` ### Properties: | Type | Property | Required? | Default | Description | | -------------------------- | ------------- | --------- | ------- | --------------------------------------------------------------------------------------------- | | string | applicationId | Yes | N/A | The ID of your application (set in the dashboard) | | string | domain | Yes | N/A | The domain that will have the DNS records configured, e.g. example.com | | array of DNSRecord objects | dnsRecords | Yes | N/A | The DNS records you wish to configure for your users | | string | subdomain | No | N/A | The subdomain that will have the DNS records configured, e.g. www | | boolean | wwwRedirect | No | false | If true, Entri will automatically set the redirect from the bare domain to the www subdomain. | # Sell White Label Source: https://developers.entri.com/sell/white-label Sell-specific theming: WhitelabelColors tokens per registrar, visibility flags, custom copy, partner branding, and IONOS/Squarespace/GoDaddy variants. ## Theme tokens (`WhitelabelColors`) Sell exposes a set of color tokens via `WhitelabelColors`. Each registrar supports a different subset. The table below shows which tokens are recognized by each registrar. | Token | IONOS | Squarespace | GoDaddy | Description | | ------------------ | :---: | :---------: | :-----: | --------------------------------------------------------------------------- | | `primary` | ✓ | — | — | Main brand color. Used for background colors throughout the flow. | | `onPrimary` | ✓ | — | — | Text / icon color rendered on top of `primary` surfaces. | | `secondary` | ✓ | ✓ | — | Secondary brand color, used for most calls-to-action (CTAs) and UI accents. | | `onSecondary` | ✓ | ✓ | — | Text / icon color rendered on top of `secondary` surfaces. | | `error` | ✓ | — | — | Error-state color (error text, error borders, validation messages). | | `border` | ✓ | — | — | Default border color for inputs, cards, and dividers. | | `priceBadge` | ✓ | ✓ | — | Background color of price badges (e.g. "First year offer"). | | `onPriceBadge` | ✓ | ✓ | — | Text color rendered on top of the price badge. | | `headerBackground` | ✓ | — | — | Header background color. Defaults to `primary` if not set. | | `interactive` | ✓ | — | — | Color for interactive elements such as radio buttons. | | `premiumBadge` | ✓ | — | — | Background color of the premium badge component. | | `onPremiumBadge` | ✓ | — | — | Text color on the premium badge. | | `activating` | ✓ | — | — | Background color of the "Search Domain" button. | | `onActivating` | ✓ | — | — | Text color on the "Search Domain" button. | GoDaddy does not support `WhitelabelColors` theming. Branding for GoDaddy is limited to partner logo and partner name (see the [GoDaddy Sell Object](#godaddy-sell-object) below). ### Theme read order Sell resolves theme tokens in the following order, with the first match winning: 1. `config.whiteLabel.theme.sell.colors` — **preferred**. Sell-scoped colors live here. 2. `config.whiteLabel.theme.colors` — **fallback**. Reused from the shared Connect theme when `theme.sell.colors` isn't provided. This lets integrators reuse a single theme object across Connect and Sell, while still giving Sell-specific overrides priority when they are set. ## Sell Registrars Entri currently partners with IONOS, Squarespace, and GoDaddy as our Sell registrars. You have the ability to choose between these partners to determine your checkout experience and functionality that you will have available. If you are a customer and have any questions on Sell registrars, please reach out to your Account Manager. If you are a registrar or reseller who is interested in partnering with Entri, please reach out to us at [partnerships@entri.com](mailto:partnerships@entri.com). ### IONOS With over 6.2 million valued customers, IONOS is the trusted partner for digital transformation for businesses across the globe. * Highly customizable user experience * FREE domain for first year offer (only for paid users) * 300+ TLDs available * Supports 7 languages and 52 countries * Offer users an email inbox with their domain * Customer support handled by IONOS [Launch demo](https://www.entri.com/demo) Sell Unified #### Whitelabeling/Branding **`whiteLabel`** (object): Options for customizing the appearance of the domain purchase flow to match your brand. * **`theme`** (object): Customizes the colors used in the purchase flow. * **`primary`** (string): Main color used in your theme. Used for background colors throughout the flow. **Example:** **`"#012939"`** * **`onPrimary`** (string): Color used for text and icons that appear on top of the primary color. **Example:** **`"#ffffff"`** * **`secondary`** (string): Color that complements the primary color and is used for secondary UI elements. **Example:** **`"#012939"`** * **`onSecondary`** (string): Color used for text and icons that appear on top of the secondary color. **Example:** **`"#ffffff"`** * **`headerBackground`** (string) (**optional**): Background color of the header. Accepts any valid CSS color code (e.g., hex, rgb). If not defined, it will default to the primary color value. **Example:** **`"#123456"`** * **`interactive`** (string) (**optional**): Color for interactive elements such as radio buttons. Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#ffcc00"`** * **`premiumBadge`** (string) (**optional**): Background color of premium badge components. Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#00ffcc"`** * **`onPremiumBadge`** (string) (**optional**): Text color on the premium badge. Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#ffffff"`** * **`priceBadge`** (string) (**optional**):This field specifies the background color of the price badge component. Accepts any valid CSS color code (e.g., hex, rgb).**Example:** **`"#ff6600"`** * **`onPriceBadge`** (string) (**optional**):This field specifies the text color on the price badge. Accepts any valid CSS color code (e.g., hex, rgb).**Example:** **`"#ffffff"`** * **`activating`** (string) (**optional**): Background color of the “Search Domain” button. Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#ff4500"`** * **`onActivating`** (string) (**optional**): Text color on the “Search Domain” button. Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#000000"`** * **`hideEntriLogo`** (boolean): Hide the Entri logo in the purchase flow. **Example:** **`true`** * **`hideCompanyLogo`** (boolean): Hide the company logo in the purchase flow. **Example:** **`true`** * **`hideCompanyName`** (boolean): Hide the company name in the purchase flow. **Example:** **`true`** * **`customCopy`** (object): Custom text to be displayed in the purchase flow. * **`initialScreen.title.{locale}`** (object): Title text that appears on the initial screen of the domain purchase flow, localized by language. **Example:** **`"Search for your domain"`** * **`initialScreen.marketingCopy.{locale}`** (object): Marketing copy that appears below the domain input field, localized by language. **Example:** **`"Your website will look even better with a custom domain!"`** Whitelabel-1.png Whitelabel 1.2.png Whitelabel-2.png Whitelabel-3.png Whitelabel-4.png #### Ionos Sell Object * **`whiteLabel.sell`** (object): Contains information specific to the cobranding and user data prefill in the domain purchase flow. * **`flowTarget`** (string): The target location where the purchase flow window will be displayed. If \_blank is specified, the flow will open in a new browser tab. **Example:** **`"_blank"`** * **`partnerLogo`** (string): The URL of the partner’s logo, which will be displayed prominently in the cobranded header. **Example:** **`"https://cdn.goentri.com/mylogo.svg"`** * **`partnerIcon`** (string): The URL of the partner’s icon, which will appear in the floating cart at the bottom of the page. **Example:** **`"https://cdn.goentri.com/myicon.svg"`** * **`partnerName`** (string): The name of the partner company, displayed within the purchase flow. **Example:** **`"My Company name"`** * **`contact`** (object): Prefilled user data to be used in the contact information form during domain creation. The following fields are supported: * **`firstName`** (string): User’s first name. **Example:** **`"John"`** * **`lastName`** (string): User’s last name. **Example:** **`"Doe"`** * **`email`** (string): User’s email address. **Example:** **`"john.doe@example.com"`** * **`phone`** (string): User’s phone number. **Example:** **`"+1234567890"`** * **`address`** (string): User’s street address. **Example:** **`"123 Main St"`** * **`zip`** (string): User’s ZIP or postal code. **Example:** **`"12345"`** * **`city`** (string): User’s city. **Example:** **`"Exampleville"`** * **`state`** (string): User’s state or province. **Example:** **`"EX"`** * **`postalCode`** (string): User’s postal code. **Example:** **`"12345"`** * **`country`** (string): User’s country. **Example:** **`"US"`** ### Squarespace World class domain registration and a clean, easy-to-use interface. Squarespace Domains makes it easy. * 300+ TLDs to choose from. * Award-winning support from Squarespace. * Default domain privacy for added security. * Instant DNS setup after purchase. * Seamless connection to your website. * Easy-to-use management dashboard. * \$0 in the first year for select TLDs (reach out to your account manager for access) [Launch demo](https://www.entri.com/demo) Search #### Squarespace Sell Object * **`whiteLabel.sell`** (object): Contains information specific to the cobranding and user data prefill in the domain purchase flow. * **`partnerLogo`** (string): The URL of the partner's logo, which will be displayed prominently in the cobranded header. **Example:** **`"https://cdn.goentri.com/mylogo.svg"`** * **`partnerName`** (string): The name of the partner company, displayed within the purchase flow. **Example:** **`"My Company name"`** * **`contact`** (object): Prefilled user data to be used in the contact information form during domain creation. The following fields are supported: * **`firstName`** (string): User's first name. **Example:** **`"John"`** * **`lastName`** (string): User's last name. **Example:** **`"Doe"`** * **`email`** (string): User's email address. **Example:** **`"john.doe@example.com"`** * **`phone`** (string): User's phone number. **Example:** **`"+1234567890"`** * **`address`** (string): User's street address. **Example:** **`"123 Main St"`** * **`zip`** (string): User's ZIP or postal code. **Example:** **`"12345"`** * **`city`** (string): User's city. **Example:** **`"Exampleville"`** * **`state`** (string): User's state or province. **Example:** **`"EX"`** * **`postalCode`** (string): User's postal code. **Example:** **`"12345"`** * **`country`** (string): User's country. **Example:** **`"US"`** #### Whitelabeling/Branding **`whiteLabel`** (object): Options for customizing the appearance of the domain purchase flow to match your brand. * **`theme`** (object): Customizes the colors used in the purchase flow. * **`secondary`** (string): Color used for buttons and secondary UI elements throughout the purchase flow (Search, Continue to Checkout, Continue to Payment, Back to App). **Example:** **`"#012939"`** * **`onSecondary`** (string): Color used for text and icons that appear on top of the secondary color. **Example:** **`"#ffffff"`** * **`priceBadge`** (string) (**optional**): This field specifies the background color of the price badge component (e.g. "First year offer"). Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#ff6600"`** * **`onPriceBadge`** (string) (**optional**): This field specifies the text color on the price badge. Accepts any valid CSS color code (e.g., hex, rgb). **Example:** **`"#ffffff"`** * **`customCopy`** (object): Custom text to be displayed in the purchase flow. * **`initialScreen.title.{locale}`** (object): Title text that appears on the initial screen of the domain purchase flow, localized by language. **Example:** **`"Find your new domain"`** * **`initialScreen.marketingCopy.{locale}`** (object): Marketing copy that appears below the title on the initial screen, localized by language. **Example:** **`"Your website will look even better with a custom domain!"`** * **`congratulations.sell.title.{locale}`** (object): Title text that appears on the order confirmation screen after a successful purchase, localized by language. **Example:** **`"Order placed"`** * **`congratulations.sell.description.{locale}`** (object): Description text that appears on the order confirmation screen below the title, localized by language. **Example:** **`"Squarespace will send you a confirmation email soon."`** SQSP Whitelabel-1.png SQSP Whitelabel-2.png SQSP Whitelabel-3.png SQSP Whitelabel-4.png SQSP Whitelabel 5.png ### GoDaddy As the #1 domain registrar in the world for over 25 years, GoDaddy makes domain ownership and management seamless and easy. * 20+ million customers worldwide trust GoDaddy with their domains * 550+ TLDs available * 24/7 award-winning, human support * Automatic DMARC set up — configured for your users * Automated DNS management * Free WHOIS privacy [Launch demo](https://www.entri.com/demo) GoDaddy domain search #### GoDaddy Sell Object * **`whiteLabel.sell`** (object): Contains information specific to the cobranding in the domain purchase flow. GoDaddy supports partner branding only; theme color tokens and contact prefill are not applied. * **`partnerLogo`** (string): The URL of the partner's logo, which will be displayed prominently in the cobranded header. **Example:** **`"https://cdn.goentri.com/mylogo.svg"`** * **`partnerName`** (string): The name of the partner company, displayed within the purchase flow. **Example:** **`"My Company name"`** #### Whitelabeling/Branding GoDaddy does not support `whiteLabel.theme` color tokens or `customCopy` overrides. Branding is limited to `partnerLogo` and `partnerName` as described in the [GoDaddy Sell Object](#godaddy-sell-object) above. # Webhooks Overview Source: https://developers.entri.com/webhooks-overview Payload envelope, signature verification, top-level fields, and links to per-product event pages. Webhooks will only be delivered to accounts set as `Admin` or `Developer` roles within the Dashboard. Entri uses webhooks to notify your backend about the status of a domain that has been [connected](/getting-started) or [sold](/sell/overview). You can configure a URL in the Entri dashboard to receive these webhook events. ## Example webhook payload When a domain has been added by a user, Entri sends the following webhook payload: ```json theme={null} { "id": "1234567890-abcdefg-1234567890", "user_id": "your-provided-user-id", "domain": "example.com", "subdomain": "shop", "provider" : "cloudflare", "type": "domain.added", "propagation_status": "pending", "dkim_status": "success", "redirection_status": "exempt", "setup_type": "automatic", "secure_status": "success", "power_status": "success", "monitor_status": "pending", "dmarc_updated": "exempt", "cname_target": "my.saascompany.com", "purchased_domains": ["domain1.com", "domain2.com", "domainN.com"], "free_domain": true, "data": { "records_propagated": [ { "type": "A", "host": "smallbusiness.com", "value": "54.153.2.220", "applicationUrl": "www.example.com", "powerRootPathAccess": ["path1", "path2", "pathN"] }, { "type": "CNAME", "host": "www", "value": "smallbusiness.com" } ], "records_non_propagated": [] } } ``` ## Webhook payload breakdown The webhook payload includes several fields with nested objects, depending on the type of event. Below is a complete description of each key and value. ### Top-Level fields * **`id`**: A unique identifier for the webhook event (e.g. `"1234567890-abcdefg-1234567890"`). * **`user_id`**: The ID of the user who initiated the domain-related action (e.g. `"your-provided-user-id"`). * **`domain`**: The domain involved in the event (e.g. `"example.com"`). * **`subdomain`**: The subdomain associated with the event, if any (e.g. `"shop"`). * **`provider`**: The domain's provider associated with the event (e.g. `"cloudflare"`). For event types and status fields, see the per-product events pages: [Connect events](/connect/events), [Sell events](/sell/events), [Power events](/power/events), [Secure events](/secure/events), [Monitor events](/monitor/events). ## Examples of key and value pairs * `"type": "domain.added"`: An event type indicating that a domain has been added. * `"propagation_status": "pending"`: DNS record propagation is still in progress. * `"secure_status": "success"`: SSL configuration completed successfully. * `"power_status": "failed"`: The advanced feature configuration process has failed. * `"cname_target": "my.saascompany.com"`: The CNAME target for Entri Power or Secure configuration. ## Webhook updates If any part of the webhook object is updated, subsequent webhooks will include ALL fields. Example update: ```json theme={null} { "id": "1234567890-abcdefg-1234567890", "propagation_status": "success", "updated_objects": ["propagation_status"] } ``` ## Propagation retries If the DNS records are not fully propagated on the first attempt, Entri will retry every 10 seconds for the first 2 mins, and then at increasing intervals (1 minute, 2 minutes, 4 minutes, 8 mins, ...etc). This process will continue for up to 72 hours. After 72 hours, if the records are still not propagated, a webhook with `"type": "domain.propagation.timeout"` will be sent. ## Connection failures retries Webhooks automatically retries up to 3 times if something goes wrong. If all 3 retries fail, a warning email will be sent (limited to 1 per day). ## DKIM status notes The `dkim_status` field is set to `exempt` if: * The user does not use Google or Microsoft email providers. * The `Enable support for DKIM` setting is disabled for your `applicationId`. ## Overview and Webhook Structure **Included in all tiers.** All webhook notifications include the following headers and follow the structure described below. All webhook notifications will have the following header in the Request: `"User-Agent": "Entri-Webhook"`. This is the list of keys you will receive on a webhook notification: ```JSON JSON theme={null} { "id": "028b5078-8fed-4ffb-8e3e-3e6e6d8214b4", "user_id": "sample-user", "domain": "smallbusiness.com", "provider": "ionos.com", "type": "domain.added", "propagation_status": "success", "dkim_status": "success", "redirection_status": "exempt", "ssl_status": "success", "setup_type": "automatic", "secure_status": "success", //Only for Entri Secure usage "power_status" : "success", //Only for Entri Power usage "cname_target": "my.saascompany.com", //Only for Entri Power and Secure usage "purchased_domains": ["domain1.com",...,"domainN.com"], //Only for Entri Sell usage "data": { "records_propagated": [ { "type": "A", "host": "smallbusiness.com", "value": "54.153.2.220" }, { "type": "CNAME", "host": "www", "value": "smallbusiness.com" } ], "records_non_propagated": [], "updated_objects": [ "propagation_status", "ssl_status" ] } } ``` The JSON object contains the following properties: | Property | Type | Description | | ----------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | string | Entri's ID for the webhook event | | user\_id | string | The user ID that your application can optionally send via the configuration object during setup (see the [entri.showEntri method](/connect/configuration#entrishowentriconfig)) | | domain | string | The domain name set by your user | | type | \[domain.purchased, domain.added, domain.propagation.timeout] | The event type. Options:- domain.added- domain.propagation.timeout- domain.purchased | | setup\_type | \[automatic, manual] | States if the dns record(s) was done in a manual or automatic way. Options:- automatic- manual | | propagation\_status | \[pending, success, failed, exempt] | The DNS propagation status. Options:- success- pending- failed | | dkim\_status | \[pending, success, failed, exempt] | Whether DKIM has been set up (see [Handling DKIM, SPF, and DMARC Records](/handling-dkim-spf-dmarc-records)). Options:- success- pending- failed- exempt (the feature is disabled) | | redirection\_status | \[pending, success, failed, exempt] | If the wwwRedirect feature was enabled for your applicationId, then Entri will check to confirm the status of the url redirect.Options:- success- pending- failed- exempt (the feature is disabled) | | data.records\_propagated | array of DNSRecord objects | See above | | data.records\_non\_propagated | array of DNSRecord objects | See above | | updated\_objects | array of strings | The object properties that have been updated since the last webhook event. | | secure\_status | \[pending, success, failed, exempt] | Whether an SSL certificate has been provisioned (see [Provisioning SSL Certificates](/secure/overview)). Options:- success- pending- failed- exempt (the feature is disabled) | | power\_status | \[pending, success, failed, exempt] | Whether a domain has been powered (see [Powering domains](/power/overview)). Options:- success- pending- failed- exempt (the feature is disabled) | | cname\_target | string | Account's configured cname\_target value for Entri Power and Secure or empty string if it doesn't apply. | | purchased\_domains | array of strings | List of purchased domains along the Entri Sell flow or empty array if not an Entri Sell flow. | | provider | string | The domain's provider associated with the event (e.g. `"cloudflare"`). | ## Webhook Management Endpoints These endpoints are designed to help partners retrieve or re-trigger webhook events programmatically. Each request requires a valid jobId, which can be captured from the [`onSuccess`](/connect/events#onsuccess-callback-event) browser event. ### Trigger propagation re-check (async) When this endpoint is called, Entri performs an immediate DNS propagation re-check for the specified Connect flow and **delivers the latest result via webhook**. The endpoint is still **asynchronous**: it only confirms that the check was queued; the propagation result is sent exclusively through the webhook, and no continuous propagation checks happen after this manual re-check. > **Tip:** The `jobId` required for this endpoint can be obtained from the [`onSuccess`](/connect/events#onsuccess-callback-event) browser event once the flow finishes successfully. ```txt theme={null} POST https://api.goentri.com/connect/propagation/recheck/:job_id Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Alternative UI-based option This action can also be triggered directly from the Customer Dashboard, under the Webhook logs section. In front of each log, users will find the “Check propagation again” button, which performs the same propagation re-check as this endpoint. #### Headers | Header | Required? | Description | | ------------- | --------- | -------------------------------------------- | | Authorization | Yes | Bearer token (JWT) for authentication. | | applicationId | Yes | Your Application ID as set in the dashboard. | #### Path Parameters | Parameter | Type | Required? | Description | | --------- | ------------- | --------- | ------------------------------------------------------------------------------------------ | | job\_id | string (UUID) | Yes | The Connect flow identifier (returned by initialize and included in all related webhooks). | #### Request Body *None.* All inputs are provided via path parameters and headers. #### Response * **202 Accepted** if the re-check was queued successfully. ```json theme={null} { "jobId": "9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de", "queued": true, "message": "Propagation re-check has been queued. A webhook will be sent with the result.", "enqueuedAt": "2025-11-05T18:22:00Z" } ``` #### Webhook behavior * Performs one immediate propagation re-check and sends a webhook with the **latest** propagation status; no continuous propagation retries follow this manual re-check. * Includes `"manual_retry": true` in the webhook **body**. * Follows the standard delivery policy with up to **3 attempts** if the receiver is unavailable. * **Logs a new webhook attempt** entry; existing webhook logs are not overridden. * Uses the same execution codepath as the private Dashboard action for this functionality. #### Cooldown To avoid abuse and accidental hot-polling, this endpoint can be called **once every 5 minutes per job**.\ If called sooner, it returns: * **429 Too Many Requests** with message: `"You can only trigger the check once every 5 mins"`\ (Servers may also include `Retry-After: 300`.) #### Error Responses | HTTP | Example Body | Description | | ---: | ------------------------------------------------------------------- | ------------------------------------------------------------ | | 400 | `{ "message": "Invalid job_id format" }` | Malformed UUID. | | 401 | `{ "message": "Unauthorized" }` | Invalid or missing JWT. | | 403 | `{ "message": "Forbidden" }` | The `job_id` does not belong to the calling `applicationId`. | | 404 | `{ "message": "Job not found" }` | Unknown `job_id`. | | 429 | `{ "message": "You can only trigger the check once every 5 mins" }` | Called within the 5‑minute cooldown window. | | 500 | `{ "message": "Internal Server Error" }` | Unexpected server error. | #### Example cURL ```bash theme={null} curl -s -X POST \ "https://api.goentri.com/connect/propagation/recheck/9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de" \ -H "Authorization: Bearer " \ -H "applicationId: " ``` ### Get last webhook by job ID This endpoint allows you to retrieve the last webhook event sent for a specific Connect flow, based on the `job_id`. It is useful for debugging or validating the final propagation result of a specific domain connection. > **Tip:** You can obtain the `jobId` value directly from the [`onSuccess`](/connect/events#onsuccess-callback-event) browser event, which is triggered when the user reaches the Congratulations screen. ```txt theme={null} GET https://api.goentri.com/connect/webhooks/last/:job_id Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` #### Headers | Header | Required? | Description | | ------------- | --------- | -------------------------------------------- | | Authorization | Yes | Bearer token (JWT) for authentication. | | applicationId | Yes | Your Application ID as set in the dashboard. | #### Path Parameters | Parameter | Type | Required? | Description | | --------- | ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------ | | job\_id | string (UUID) | Yes | The UUID of the Connect flow. The same job\_id returned in the initialize endpoint and included in all related webhooks. | #### Sample Response ```json theme={null} { "id": "d6a6b6c4-45f3-42a2-8bb1-3c92debf47e2", "job_id": "9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de", "domain": "example.com", "type": "domain.propagation.success", "manual_retry": false, "status": "delivered", "sentAt": "2025-11-05T18:22:00Z", "payload": { "domain": "example.com", "job_id": "9c128fe4-63cd-4ec4-9ae8-8a9d06c0e6de", "propagation_status": "success", "records_checked": [ { "type": "CNAME", "host": "www", "value": "myapp.com" } ] } } ``` #### Error Responses | HTTP | Example Body | Description | | ---- | ---------------------------------------- | --------------------------------------------------------- | | 400 | `{ "message": "Invalid job_id format" }` | Malformed or missing job\_id. | | 401 | `{ "message": "Unauthorized" }` | Invalid or missing JWT. | | 403 | `{ "message": "Forbidden" }` | The job\_id does not belong to the calling applicationId. | | 404 | `{ "message": "Webhook not found" }` | No webhook log was found for the given job\_id. | | 500 | `{ "message": "Internal Server Error" }` | Unexpected server error. | #### Notes * This endpoint is **read-only** and returns the last logged webhook event associated with the provided job\_id. * It retrieves data from the webhook logs table and does **not** resend or trigger new webhooks. * Only returns the **most recent** webhook entry for that job. ## Provider Health Returns the status of all the providers for an application ID that Entri supports. ``` GET https://api.goentri.com/providers_health Header "Authorization: [JWT authorization]" Header "applicationId: [yourApplicationID]" ``` ### Response body ```JSON JSON theme={null} { "message": "success", "data": [ { "provider": "IONOS", "enabled": true }, { "provider": "Enom", "enabled": true }, { "provider": "Amazon Route 53", "enabled": true }, ... ] } ``` ## Entri Webhook Signature Verification ## Enforcing Webhook Source IP (Optional) All webhook requests from Entri are now sent from a fixed static IP address: `3.14.77.245` For an additional layer of security, you may allow webhook traffic only from this IP within your firewall, API gateway, or reverse proxy. This ensures that only Entri can reach your webhook endpoint, even before signature validation occurs, providing a strong network-level safeguard against unauthorized requests. The fixed IP address (`3.14.77.245`) applies only to production webhook deliveries. The **Dashboard webhook tester** does not originate from this IP address. If you are testing webhooks using the Dashboard tester, you must temporarily disable IP allowlisting or your endpoint may reject the request. ## Signature Timestamp Header The `Entri-Timestamp` header contains the Unix timestamp (in seconds) for when the webhook was generated. It is used by both Signature V2 and Signature V3 verification. This enables servers to enforce freshness windows and reject replayed requests. ## Signature Verification Methods Entri sends all three signature headers on every webhook request — they are additive and do not replace each other. You may verify any or all of them; we recommend verifying at least V3 for new integrations. | Header | Algorithm | Signed content | Notes | | -------------------- | ---------------------- | -------------------------------------------------------------- | ---------------------------------------------- | | `Entri-Signature` | SHA-256 | `sha256(webhook_id + client_secret)` | V1 — legacy | | `Entri-Signature-V2` | SHA-256 | `sha256(webhook_id + timestamp + client_secret)` | Replay-protected | | `Entri-Signature-V3` | HMAC-SHA256 (RFC 2104) | `hmac_sha256(key=client_secret, msg="{timestamp}.{raw_body}")` | **Recommended** — body-bound, replay-protected | | `Entri-Timestamp` | — | Unix timestamp (seconds) | Used by V2 and V3 | ### Signature V3 (Recommended) `Entri-Signature-V3` uses true **HMAC-SHA256** (RFC 2104). It provides two security improvements over V1 and V2: * **Body binding**: the HMAC is computed over the raw HTTP request body, so any tampering with the payload invalidates the signature. V1 and V2 sign only identifiers and timestamps, not the body contents. * **Replay protection**: the `Entri-Timestamp` value is embedded in the signed message, so a captured valid request cannot be replayed after 300 seconds. #### Header value format ``` Entri-Signature-V3: sha256= ``` #### How to verify 1. Read `Entri-Timestamp`. Reject the request if `abs(now - timestamp) > 300` seconds. 2. Build the signed payload by concatenating: the timestamp string, a literal `.`, then the **raw HTTP request body bytes**. 3. Compute `HMAC-SHA256` with your `client_secret` as the key over that payload. 4. Hex-encode the result and prepend `sha256=`. 5. Compare to the `Entri-Signature-V3` header value using a constant-time comparison. Always compute the HMAC over the **raw HTTP request body bytes**, before any JSON parsing. Do not parse the JSON body and re-serialize it — different libraries produce different byte sequences (whitespace, key ordering, number formatting), which will cause verification to fail. #### Verification examples ```python Python theme={null} import hmac import hashlib import time def verify_entri_signature_v3( raw_body: bytes, headers: dict, client_secret: str, max_age_seconds: int = 300, ) -> bool: timestamp = headers.get("Entri-Timestamp", "") signature = headers.get("Entri-Signature-V3", "") try: if abs(time.time() - int(timestamp)) > max_age_seconds: return False except (ValueError, TypeError): return False signed_payload = f"{timestamp}.".encode() + raw_body expected = "sha256=" + hmac.new( key=client_secret.encode(), msg=signed_payload, digestmod=hashlib.sha256, ).hexdigest() return hmac.compare_digest(expected, signature) ``` ```javascript Node.js theme={null} const crypto = require("crypto"); function verifyEntriSignatureV3(rawBody, headers, clientSecret, maxAgeSeconds = 300) { // Header names are lowercased by most Node.js HTTP frameworks const timestamp = headers["entri-timestamp"] ?? headers["Entri-Timestamp"] ?? ""; const signature = headers["entri-signature-v3"] ?? headers["Entri-Signature-V3"] ?? ""; const ts = parseInt(timestamp, 10); if (isNaN(ts) || Math.abs(Date.now() / 1000 - ts) > maxAgeSeconds) { return false; } // rawBody must be the original Buffer — do not pass a re-serialized JSON string const body = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody); const signedPayload = Buffer.concat([Buffer.from(`${timestamp}.`), body]); const expected = "sha256=" + crypto .createHmac("sha256", clientSecret) .update(signedPayload) .digest("hex"); try { return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature)); } catch { return false; } } ``` ```php PHP theme={null} $maxAgeSeconds) { return false; } // Concatenate timestamp, literal dot, and raw body — do not JSON-decode first $signedPayload = $timestamp . ‘.’ . $rawBody; $expected = ‘sha256=’ . hash_hmac(‘sha256’, $signedPayload, $clientSecret); return hash_equals($expected, $signature); } ``` `Entri-Signature` (V1) and `Entri-Signature-V2` continue to be sent alongside V3. Existing integrations that verify V1 or V2 will keep working without any changes. V3 is purely additive — add it to new integrations and migrate existing ones at your convenience. ### Signature V2 `Entri-Signature-V2` adds timestamp binding to V1, making each signature unique and time-bound. It is computed as `SHA256(webhook_id + timestamp + client_secret)` with no separators between the values. The V2 signature is a plain SHA-256 digest of `webhook_id ‖ timestamp ‖ client_secret`. It is **not** an RFC 2104 HMAC-SHA256. Placing the secret at the end of the input avoids the length-extension weakness of the `H(secret ‖ msg)` construction. #### How to verify 1. Read the webhook payload’s `id` field. 2. Read the `Entri-Timestamp` header (epoch seconds). 3. Build the canonical message: `webhook_id + timestamp + client_secret` (concatenated, no separators). 4. Compute `SHA256(canonical_message)` and hex-encode it. 5. Compare the result to the `Entri-Signature-V2` header using a constant-time comparison. 6. Enforce a freshness window (for example, 5 minutes) using the timestamp. #### Verification example (Python) ```python theme={null} import hashlib import hmac import time def verify_entri_signature_v2( webhook_id: str, client_secret: str, received_signature: str, received_timestamp: str, max_age_seconds: int = 300, ) -> bool: try: if int(time.time()) - int(received_timestamp) > max_age_seconds: return False expected = hashlib.sha256( webhook_id.encode() + received_timestamp.encode() + client_secret.encode() ).hexdigest() return hmac.compare_digest(expected, received_signature) except (ValueError, TypeError, AttributeError): return False ``` ### Signature V1 (Legacy) `Entri-Signature` is the original signature, computed as `SHA256(webhook_id + client_secret)`. It does not include a timestamp, so it provides no replay protection. #### Verification example (Python) ```python theme={null} import hashlib import hmac def verify_entri_signature_v1( webhook_id: str, client_secret: str, received_signature: str, ) -> bool: expected = hashlib.sha256( webhook_id.encode("utf-8") + client_secret.encode("utf-8") ).hexdigest() return hmac.compare_digest(expected, received_signature) ```