Embed Texture Connect

Add Texture Connect to your web or mobile application with an SDK or the API

Texture Connect is the OAuth widget your customers use to authorize access to their energy devices. This page covers embedding it in your own application. To send a customer a link without writing code, see Create a link in the dashboard.

Choose Your Integration Method#

MethodBest for
Texture APICustom implementations with full control over the flow
Headless ConnectEnterprise: fully white-labeled experience, your own UI, Texture returns OAuth URL directly
JavaScript/TypeScript SDKWeb applications — handles the popup window and OAuth flow for you
React SDKReact applications — useCreateConnectSession hook
React Native SDKiOS/Android apps — TextureConnect component

Headless Connect#

Enterprise Feature

Headless Connect must be enabled for your organization before use. Contact your Texture Solutions team to request access.

Headless Connect gives you complete control over every screen the user sees. Instead of opening the Texture-hosted Connect UI, your application collects all necessary information, creates the connect session, and redirects the user directly to the manufacturer's OAuth page — all within your own branded experience. Texture handles the OAuth callback and forwards the result to your redirectUrl.

Prerequisites#

Before initiating a headless session:

  • A manufacturer that supports OAuth for a fully headless flow. Manufacturers that use serial number capture instead of OAuth, such as ChargePoint, EG4, Emporia, FranklinWH, and Generac, are accepted, but the customer finishes on the Texture-hosted page. See Serial number manufacturers.

  • Exactly one manufacturer in manufacturerFilters.manufacturers

  • A redirectUrl your application controls — Texture redirects here after OAuth completes

  • One of the following three request combinations for customer and site identification:

    ScenarioRequired fieldsHow site is resolved
    Existing customer with sitecustomerIdCustomer's first associated site. Use customerId + siteId to control which site.
    Existing customer, explicit sitecustomerId + siteIdExplicit siteId
    New customerFull customerInfo (firstName, lastName, email, location)New site created from customerInfo.location

    Any other combination — siteId without customerId, partial customerInfo — is rejected with a validation error.

Creating a Headless Session#

Set mode: "headless" in your POST /v1/connections request:

curl -X POST https://api.texturehq.com/v1/connections \
  -H "Content-Type: application/json" \
  -H "Texture-Api-Key: your_api_key" \
  -d '{
    "mode": "headless",
    "manufacturerFilters": { "manufacturers": ["tesla"] },
    "customerId": "cllh2buhk000008la0ln6cb80",
    "siteId": "cllh2buhk000008la0ln6cb81",
    "redirectUrl": "https://yourapp.com/oauth/callback"
  }'

Or create a new customer and site on the fly:

curl -X POST https://api.texturehq.com/v1/connections \
  -H "Content-Type: application/json" \
  -H "Texture-Api-Key: your_api_key" \
  -d '{
    "mode": "headless",
    "manufacturerFilters": { "manufacturers": ["tesla"] },
    "customerInfo": {
      "firstName": "Jane",
      "lastName": "Smith",
      "email": "jane@example.com",
      "location": {
        "streetOne": "123 Main St",
        "city": "Austin",
        "state": "TX",
        "postalCode": "78701",
        "country": "US"
      }
    },
    "redirectUrl": "https://yourapp.com/oauth/callback"
  }'

Response#

{
  "customerId": "cllh2buhk000008la0ln6cb80",
  "linkUrl": "https://connect.texturehq.com/link_...",
  "linkToken": "link_e47b6d39dd3e6b3ab55969f55dc88ae6",
  "linkSessionId": "cllh2buhk000008la0ln6cb82",
  "expiresAt": "2026-04-20T12:30:00Z",
  "siteId": "cllh2buhk000008la0ln6cb81",
  "authorization": {
    "type": "oauth_redirect",
    "url": "https://auth.tesla.com/oauth2/v3/authorize?client_id=..."
  }
}
FieldDescription
authorization.typeoauth_redirect for OAuth manufacturers, or user_input for manufacturers that use serial number capture.
authorization.urlThe manufacturer's OAuth authorization URL. Redirect the user here immediately — sessions expire, so don't delay. Present only when authorization.type is oauth_redirect.
expiresAtWhen this session expires (ISO 8601). Redirect the user to authorization.url before this time or the session will be invalid.
customerIdTexture customer ID — existing or newly created.
siteIdThe site associated with this session.
linkSessionIdSession identifier — useful for troubleshooting with Texture support.
linkUrlURL to the Texture-hosted Connect UI. For OAuth manufacturers, use authorization.url instead. For user_input, send the customer here.
linkTokenInternal token that identifies this session. Not needed for headless flows.

Serial number manufacturers#

For manufacturers that use serial number capture instead of OAuth, the response has authorization.type set to user_input and no authorization.url. Send the customer to linkUrl, where they enter their device's serial number on the Texture-hosted page. When they continue from the success screen, Texture sends them to your redirectUrl with texture_scoped_key and texture_customer_id.

Handling the OAuth Callback#

After OAuth completes, Texture redirects to your redirectUrl with these query parameters:

On success:

ParameterDescription
texture_scoped_keyBearer token for this connection — store securely, use it for subsequent device API calls.
texture_customer_idTexture customer ID.
texture_existing_devicestrue if this customer already had connected devices. Omitted otherwise.

On failure:

Texture redirects with a texture_error query parameter set to one of these values:

texture_error valueDescription
connect_session_expiredSession timed out before OAuth completed.
connect_session_already_completedOAuth was already completed for this session (replay protection).
oauth_state_mismatchCSRF state validation failed.
connection_failedToken exchange or device connection creation failed. May be transient — retry may succeed.
device_incompatibleUser's device doesn't support the requested permissions or has no compatible devices.
subscription_requiredDevice account lacks an active connected-services subscription with the manufacturer.
unknown_errorUnrecognized error. A companion texture_error_raw parameter contains the original manufacturer error string for debugging.
access_deniedUser declined authorization.
server_errorManufacturer authorization server error. Retry may succeed.
temporarily_unavailableManufacturer server temporarily unavailable. Retry after a delay.
const params = new URLSearchParams(window.location.search);
const error = params.get("texture_error");
 
if (error) {
  const errorRaw = params.get("texture_error_raw"); // only present when error === "unknown_error"
  console.error("OAuth failed:", error, errorRaw);
} else {
  const scopedKey = params.get("texture_scoped_key");
  const customerId = params.get("texture_customer_id");
  // Store scopedKey securely and use it for subsequent API calls
}

Configuration Reference#

Session Options#

FieldRequiredDescription
redirectUrlYesURL where users land after completing the flow. For web: https://mycompany.com/app. For mobile: a deep link like myapp://device/connected. Texture appends texture_scoped_key as a query parameter.
referenceIdNoYour identifier for this customer — a userId, site ID, or anything you use to look them up later. If not supplied, Texture generates one of the form texture-generated:<uuidv7>.
tagsNoArray of strings for grouping devices (e.g. ["boston", "program-A"]). Tags are editable later via API.
clientNameNoYour company or application name as it appears in the Connect UI. Defaults to your organization name.
customerIdNoAn existing Texture customer ID. Takes priority over customerInfo and referenceId. If the customer has multiple sites, Texture uses their first associated site — pair with siteId to specify which one.
siteIdNoID of an existing site to associate with this session. In standard mode, takes highest priority over site resolved from customerId or customerInfo.location. In headless mode, must be paired with customerId.
modeNostandard (default) or headless. Headless returns an OAuth URL instead of showing the Texture-hosted UI.

manufacturerFilters#

Restrict which manufacturers appear in the Connect flow.

PropertyDescriptionExample
manufacturersLimit to specific manufacturers. If exactly 1 is specified, the manufacturer selection screen is skipped.["enphase", "tesla"]
deviceTypesLimit to manufacturers that have devices of these types.["battery"]

customerInfo#

Pre-populate customer details. If firstName, lastName, email, and location are all supplied, the customer info collection form is skipped entirely.

PropertyDescriptionExample
firstNameCustomer first name"John"
lastNameCustomer last name"Doe"
emailCustomer email"example@example.com"
phoneCustomer phone"555-555-0100"
locationCustomer site address — all fields required if provided{"streetOne": "1600 Pennsylvania Avenue NW", "city": "Washington", "state": "DC", "postalCode": "20500", "country": "US"}

Quick Start: SDK#

Installation#

JavaScript/TypeScript#

npm install @texturehq/connect-sdk

or with Yarn:

yarn add @texturehq/connect-sdk

You can also load via CDN. Pin an exact version so a future release can't change the flow under you:

<script src="https://unpkg.com/@texturehq/connect-sdk@0.8.1/dist/connect-sdk.iife.js"></script>

React#

npm install @texturehq/react-connect-sdk

or with Yarn:

yarn add @texturehq/react-connect-sdk

React Native#

npm install @texturehq/react-native-connect-sdk

or with Yarn:

yarn add @texturehq/react-native-connect-sdk

Usage#

JavaScript/TypeScript usage#

import { createConnectSession } from "@texturehq/connect-sdk";
 
const texture = createConnectSession({
  connectApiKey: "<connect-api-key>",
  connectOptions: {
    referenceId: "<reference-id-from-your-system>",
    clientName: "Name for your application",
    redirectUrl: "https://your-application.com/redirect",
    tags: ["tag1", "tag2"],
    manufacturerFilters: { manufacturers: ['honeywell', 'daikin'] },
    customerInfo: {
      email: "example@example.com",
      phone: "555-555-0100",
      firstName: "John",
      lastName: "Doe",
    },
  },
  onSuccess: ({ scopedKey }) => {
    // Send scopedKey to your backend and store it — do not log it.
    // It is a bearer token for this customer's devices. See /api/scoped-keys.
  },
  onError: ({ type, reason }) => {
    // type 'popup-blocked' — popup window was blocked
    // type 'popup-closed' — user closed popup before completing the flow
  },
});
 
// Open the popup
texture.open();
 
// Optional: control popup dimensions
texture.open({ width: 500, height: 600 });
 
// Close programmatically
texture.close();
 
// Get the connect URL
const url = texture.url;

What to do with the scopedKey: send it to your backend and store it against the customer record. The scoped key is a bearer token that lets you call the Texture API for that specific customer's devices — it does not grant access to other customers' data. See Scoped Keys for usage details.

React usage#

import { useCreateConnectSession } from "@texturehq/react-connect-sdk";
 
function App() {
  const { open } = useCreateConnectSession({
    connectApiKey: "<connect-api-key>",
    connectOptions: {
      referenceId: "<reference-id-from-your-system>",
      clientName: "Name for your application",
      redirectUrl: "https://your-application.com/redirect",
      tags: ["tag1", "tag2"],
      manufacturerFilters: { manufacturers: ['honeywell', 'daikin'] },
      customerInfo: {
        email: "example@example.com",
        phone: "555-555-0100",
        firstName: "John",
        lastName: "Doe",
      },
    },
    onSuccess: ({ scopedKey }) => {
      // Send scopedKey to your backend and store it against the customer record.
      // It is a bearer token scoped to this customer's devices — see /api/scoped-keys.
    },
    onError: ({ type, reason }) => {
      // type 'popup-blocked' — popup window was blocked
      // type 'popup-closed' — user closed popup before completing the flow
    },
  });
 
  return (
    <button onClick={() => open()}>Connect your device</button>
  );
}

React Native usage#

Set redirectUrl to your app's custom URL scheme (e.g. myapp://).

Note

The React Native SDK uses positional arguments for callbacks, not objects. onSuccess receives (scopedKey: string) and onError receives (type: string, reason: string) — not the destructured form used in the JS/React SDKs.

import { TextureConnect } from '@texturehq/react-native-connect-sdk';
import { Text } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
 
export default function ConnectScreen() {
  return (
    <GestureHandlerRootView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <TextureConnect
          connectApiKey="<YOUR_CONNECT_API_KEY>"
          connectOptions={{
            clientName: 'Name for your application',
            referenceId: '123',
            redirectUrl: 'example://',
            manufacturerFilters: { manufacturers: ['honeywell', 'daikin'] },
            customerInfo: {
              email: "example@example.com",
              phone: "555-555-0100",
              firstName: "John",
              lastName: "Doe",
            },
          }}
          onError={(type, reason) => { /* handle error */ }}
          onSuccess={(scopedKey) => { /* send scopedKey to your backend — do not log */ }}
        >
            <Text>Tap here to connect a device.</Text>
        </TextureConnect>
    </GestureHandlerRootView>
  );
}

Open Source Examples#

See working examples in our examples repository.