# How to Integrate Retool with Oracle Eloqua

- Tool: Retool
- Difficulty: Advanced
- Time required: 35 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to Oracle Eloqua using a REST API Resource with Basic Auth or OAuth 2.0 and pod-based dynamic base URLs. Eloqua's enterprise MAP requires a pod discovery step to determine the correct API hostname for your instance. Once connected, you can build marketing ops dashboards managing contacts, campaigns, forms, and lead scoring for large enterprise deployments.

## Build an Enterprise Marketing Automation Panel with Oracle Eloqua and Retool

Oracle Eloqua is the MAP of choice for large B2B enterprises managing complex, multi-touch marketing programs across thousands of contacts. Its native interface is comprehensive but optimized for marketers creating campaigns — operations and RevOps teams that need cross-campaign reporting, bulk contact updates, lead scoring visibility, or combined Eloqua-CRM views quickly find that Retool provides a more efficient internal tool layer.

The integration has one significant technical complexity compared to simpler APIs: Eloqua uses a pod-based hosting architecture. Every Eloqua customer's data is hosted on a specific pod server, and your API calls must target that pod's URL rather than a generic Eloqua endpoint. You determine your pod's base URL by making an initial login call to login.eloqua.com, which returns your instance's correct API hostname. This pod discovery step must be completed before configuring your Retool REST API Resource's base URL.

Once past pod discovery, Eloqua's REST API v2.0 provides full access to its data model: contacts, accounts, campaigns, email groups, custom objects, program steps, and lead scoring models. The API also includes a Bulk API for exporting large datasets — essential for enterprise accounts with millions of contacts where the standard REST endpoints would time out or hit pagination limits. For RevOps teams, combining Eloqua contact and campaign data with Salesforce CRM data in Retool creates a unified pipeline view that neither system provides natively.

## Before you start

- An Oracle Eloqua account with API access (requires Eloqua Standard or Enterprise edition)
- Eloqua API credentials: company name, username, and password (for Basic Auth) or an OAuth 2.0 application configured in Eloqua
- A Retool account (Cloud or self-hosted) with permission to create Resources
- Basic understanding of Eloqua's data model (contacts, campaigns, assets, custom objects)
- Your Eloqua pod URL, discovered via the login.eloqua.com endpoint as described in this guide

## Step-by-step guide

### 1. Perform Eloqua pod discovery to find your API base URL

Eloqua's most important setup requirement — and what differentiates it from most other SaaS APIs — is pod discovery. Eloqua hosts customer data across multiple pod servers (secure.eloqua.com, secure2.eloqua.com, etc.), and the correct pod for your organization is determined dynamically. If you use the wrong base URL, every API call will fail with an authorization error or redirect. To discover your pod URL, make a GET request to https://login.eloqua.com/id with Basic Auth credentials. The credentials format is: username must be composed of three parts: your Eloqua company name, a backslash, and your username — for example, if your company is 'Acme Corp' and your username is 'john.doe', the credential becomes 'Acme Corp\john.doe'. In Retool, you can test this using a temporary REST API Resource with Base URL https://login.eloqua.com and Basic Auth using this combined username format and your password. Run a GET request to /id. The response will contain a 'urls' object with 'apis' nested inside — look for 'apis.rest.standard' which gives you the full REST API base URL, and 'apis.rest.bulk' for the Bulk API. The standard REST base URL looks like https://secure.eloqua.com/API/REST/2.0 or https://secure2.eloqua.com/API/REST/2.0 — the part before '/API/REST/2.0' is your pod's hostname. Save this URL. All subsequent Retool API queries must use this pod-specific base URL. Hardcode the discovered URL in your Retool resource, or store it as a configuration variable if you want to update it without editing the resource.

```
{
  "method": "GET",
  "path": "/id",
  "base_url": "https://login.eloqua.com",
  "auth": "Basic Auth",
  "username_format": "{company_name}\\{username}",
  "note": "Response contains urls.apis.rest.standard = your pod-specific base URL"
}
```

**Expected result:** The pod discovery request returns a JSON object containing your Eloqua instance's specific API base URL, site ID, and pod identifier. You have the correct base URL to use in your main Retool resource.

### 2. Create a REST API Resource with Eloqua Basic Auth

With your pod URL identified, configure the main Retool REST API Resource for Eloqua. Navigate to the Resources tab in Retool and click '+ Create new'. Select 'REST API'. In the Name field, enter 'Oracle Eloqua API'. For the Base URL, enter the pod-specific REST API base URL discovered in Step 1 — for example, https://secure.eloqua.com/API/REST/2.0. For Authentication, select 'Basic Auth'. In the username field, enter your Eloqua credentials in the required format: {company_name}\{username} — for example, Acme Corp\john.doe. Note that the backslash is a literal character in this context (not a line break). In the password field, enter your Eloqua password. Add default headers: Content-Type set to application/json and Accept set to application/json. Click 'Save resource'. Retool will attempt to verify the connection. If the test succeeds, you are ready to write queries. If you prefer OAuth 2.0 over Basic Auth (more appropriate for production enterprise deployments), you must first register an OAuth application in Eloqua (Settings → Administration → Platform Extensions → Apps → Register App). Configure the app to receive an Authorization Code grant, set the redirect URI to Retool's OAuth callback URL, and use the resulting Client ID and Client Secret in Retool's OAuth 2.0 authentication configuration. Store all credentials — password or client secret — as secret configuration variables in Retool rather than entering them directly in the resource fields.

**Expected result:** A saved REST API Resource named 'Oracle Eloqua API' with your pod-specific base URL and Basic Auth credentials is available in Retool's Resources list.

### 3. Write queries to fetch contacts and campaigns

Open your Retool app and create queries targeting your Eloqua resource. Create a contacts search query: Method GET, path /contacts/search. Eloqua's search endpoint accepts a POST body with search criteria for more complex filtering — for basic searching, use GET /contacts with params: search set to {{ searchInput.value }} (searches against email, first name, and last name), count set to 100 for page size, and page set to {{ Math.ceil(contactsTable.paginationOffset / 100) + 1 || 1 }}. Eloqua's pagination uses page numbers rather than offsets. Name this query getContacts. Create a campaigns query: Method GET, path /assets/campaigns, with params count set to 100, page set to 1, depth set to 'partial' (returns key fields without full nested objects, which significantly reduces response size). Name this query getCampaigns. Create a contact detail query: Method GET, path /contacts/{{ contactsTable.selectedRow.id }} with depth set to 'complete' to retrieve all field data for the selected contact. Name this query getContactDetail. Run each query to verify the response structure — Eloqua wraps responses in objects with 'elements' as the data array and 'total', 'pageSize', 'page' for pagination metadata.

```
{
  "method": "GET",
  "path": "/contacts",
  "params": {
    "search": "{{ searchInput.value }}",
    "count": "100",
    "page": "{{ Math.ceil((contactsTable.paginationOffset || 0) / 100) + 1 }}",
    "depth": "minimal",
    "orderBy": "createdAt desc"
  }
}
```

**Expected result:** The getContacts query returns a JSON object with an 'elements' array of contact objects and pagination metadata. The getCampaigns query similarly returns campaign data in an 'elements' array.

### 4. Transform responses and build the marketing ops dashboard

Eloqua's API returns contact and campaign data with nested field value arrays and custom field dictionaries that require transformation before binding to Retool Tables. In the getContacts query, click 'Advanced' → toggle 'Transform results'. Write a transformer that maps each element in data.elements to a flat row. Eloqua contacts have a fieldValues array where each item is { id, value } — you need to identify field IDs for key contact properties (email address, first name, last name, company, lead score) from your Eloqua account's field definitions. Alternatively, use the top-level shorthand fields that Eloqua provides on contact objects: emailAddress, firstName, lastName, createdAt, updatedAt. After building transformers, drag a Table component onto the canvas. Set its Data source to {{ getContacts.data }}. Enable server-side pagination and set the total row count to {{ getContacts.rawData.total }}. Add a search TextInput that filters the getContacts query. For the campaigns dashboard, add a second Table bound to a transformer on getCampaigns, showing campaign name, type (Email, Multi-Channel), status (Active, Draft, Completed), created date, and scheduled send date. Below the campaigns table, add a Bar Chart component that displays campaign performance metrics fetched by a separate getCampaignMetrics query for the selected campaign. For complex multi-segment reporting, combining Eloqua data with Salesforce CRM records, and building custom lead routing workflows, RapidDev's team can help architect your enterprise Retool marketing operations solution.

```
// Transformer: flatten Eloqua contact list
const elements = data.elements || [];
return elements.map(contact => ({
  id: contact.id,
  email: contact.emailAddress || 'N/A',
  first_name: contact.firstName || '',
  last_name: contact.lastName || '',
  full_name: `${contact.firstName || ''} ${contact.lastName || ''}`.trim() || 'N/A',
  company: contact.accountName || 'N/A',
  lead_score: contact.leadScore || 0,
  subscription_status: contact.subscriptionStatus || 'Unknown',
  created: contact.createdAt
    ? new Date(parseInt(contact.createdAt) * 1000).toLocaleDateString()
    : 'N/A',
  updated: contact.updatedAt
    ? new Date(parseInt(contact.updatedAt) * 1000).toLocaleDateString()
    : 'N/A'
}));
```

**Expected result:** The contacts Table displays flat rows with name, email, company, and lead score. The campaigns Table shows all marketing campaigns with status and type. Pagination works correctly with Eloqua's page-based pagination.

### 5. Implement contact updates and handle Eloqua's Bulk API for large exports

For write operations — updating contact fields, adjusting lead scores, or changing subscription status — create PUT queries targeting the contact endpoint. Method PUT, path /contacts/{{ contactsTable.selectedRow.id }}, Body type JSON with the fields to update: { "fieldValues": [{ "id": "FIELD_ID", "value": "{{ newValueInput.value }}" }] }. You will need the numeric field IDs from Eloqua's field definitions — query GET /contacts/fields to get a list of all custom and standard fields with their IDs. For large-scale exports (thousands of contacts matching a segment), Eloqua's standard REST API paginates to a maximum of a few thousand records and can be slow. Use the Eloqua Bulk API instead: this is a separate API with base URL ending in /API/bulk/2.0. The Bulk API uses a two-step export process: first POST an export definition to /contacts/exports specifying which fields to include and the filter criteria, then POST to start the sync with { "status": "pending" }, poll the sync status endpoint until it shows 'success', then GET the data from the export's data endpoint. Build this as a multi-step Retool Workflow rather than an app query — use Resource Query blocks for each API call and a Loop block to poll until the sync is complete. This approach handles Eloqua accounts with millions of contacts efficiently.

```
// Eloqua Bulk API export definition (POST to /contacts/exports)
{
  "name": "Retool Contact Export - {{ new Date().toISOString() }}",
  "fields": {
    "Email Address": "{{Contact.Field(C_EmailAddress)}}",
    "First Name": "{{Contact.Field(C_FirstName)}}",
    "Last Name": "{{Contact.Field(C_LastName)}}",
    "Lead Score": "{{Contact.Field(C_Lead_Score1)}}",
    "Company": "{{Contact.Field(C_Company)}}"
  },
  "filter": "'{{Contact.Field(C_Lead_Score1)}}' > '50'",
  "areSystemTimestampsIncluded": true
}
```

**Expected result:** Contact update queries successfully modify Eloqua contact fields. The Bulk API export workflow initiates an export job, waits for completion, and downloads the data for display in Retool — handling exports of any size without pagination constraints.

## Best practices

- Always perform pod discovery before finalizing your Retool resource base URL — do not assume your Eloqua instance is on secure.eloqua.com. Store the discovered pod URL as a configuration variable in Retool.
- Create a dedicated Eloqua API user account with principle-of-least-privilege permissions rather than using an admin account — this limits blast radius if credentials are compromised and makes API audit logs more meaningful.
- Use Eloqua's depth parameter strategically: depth=minimal for list queries (fast, returns IDs and essential fields), depth=complete for detail queries (all fields, slower). Never use depth=complete on list queries returning hundreds of records.
- For contact exports exceeding a few thousand records, always use the Eloqua Bulk API via a Retool Workflow rather than paginating through the standard REST API — the Bulk API is orders of magnitude faster for large datasets.
- Store Eloqua credentials (password or OAuth client secret) as secret configuration variables in Retool, never in query code or plain configuration variables visible in the state panel.
- Implement Retool Workflow-based monitoring for export jobs and long-running queries, with error notifications via Slack or email if a sync fails — Eloqua's Bulk API can take minutes for large exports and silently timeout.
- Combine Eloqua contact data with Salesforce opportunity data in Retool using a JavaScript transformer that joins on email address or Salesforce Account ID — this unified view is often the primary value of building a Retool layer over both systems.

## Use cases

### Build a contact lifecycle management panel

Your marketing ops team needs to view and update contact profiles, adjust lead scores, manage subscription preferences, and review contact activity timelines — all from a single interface that is faster than Eloqua's native contact editor for bulk operations. A Retool panel queries the Eloqua contacts endpoint with search filters, displays results in a Table, and provides a form for updating contact field values and lead score adjustments.

Prompt example:

```
Build a contact management panel that searches Eloqua contacts by email, company, or lead score range. Display results in a Table with columns for full name, email, company, lead score, subscription status, and last activity date. Add a detail panel that opens when a row is selected, showing all contact fields and an option to update lead score or mark as unsubscribed via PATCH request.
```

### Create a campaign performance analytics dashboard

Your RevOps team needs to compare campaign performance metrics across all active Eloqua campaigns in a single dashboard — email open rates, click rates, form submissions, and lead stage progression — rather than reviewing campaigns individually in Eloqua's interface. A Retool app queries the campaigns endpoint, fetches performance data for each, and displays a sortable comparison table and trend charts.

Prompt example:

```
Build a campaign analytics dashboard that fetches all active Eloqua campaigns with metrics including send count, open rate, click rate, total conversions, and cost per lead. Display campaigns in a sortable Table ranked by conversion rate. Add a Bar Chart comparing open and click rates across the top ten campaigns. Include date range filters to scope the analysis.
```

### Build a form submission review and routing panel

Your sales operations team receives form submissions from Eloqua landing page forms and needs to review incoming data, enrich it, and route qualified leads to specific Salesforce queues. A Retool panel queries recent Eloqua form submissions, displays the data alongside a lead scoring indicator, and provides a routing button that creates a Salesforce opportunity via a second Resource query.

Prompt example:

```
Build a form submission review panel that queries Eloqua form submissions from the last 7 days. Display each submission with form name, submitted fields, lead score at time of submission, and whether a Salesforce record exists. Add a 'Create Opportunity' button that fires a POST to a connected Salesforce REST Resource, and a 'Reject' button that marks the contact as disqualified in Eloqua.
```

## Troubleshooting

### All API requests return 401 Unauthorized despite correct credentials

Cause: The base URL is incorrect — you are calling a generic Eloqua URL rather than your instance's specific pod URL. Eloqua returns 401 for requests to the wrong pod, even with valid credentials.

Solution: Repeat the pod discovery step: make a GET request to https://login.eloqua.com/id with your Basic Auth credentials. Retrieve the 'urls.apis.rest.standard' field from the response. Update your Retool REST API Resource base URL to match this value exactly. Ensure the username format includes your company name and a backslash before your username: {CompanyName}\{username}.

### Contact search returns zero results for known contacts

Cause: The search parameter syntax does not match Eloqua's expectations. Eloqua's search parameter for GET /contacts accepts specific field-based query strings, not free-text search across all fields by default.

Solution: Try using the email address as the search value: search=emailAddress%3D'user@example.com' (URL-encoded). Alternatively, use the POST /contacts/search endpoint which accepts a structured JSON body with explicit field filters and supports more complex query criteria. Review Eloqua's API documentation for the exact search query syntax for your API version.

### API response contains fieldValues array but contact's email or name fields are empty

Cause: Eloqua stores most contact data in the fieldValues array as key-value pairs with numeric field IDs, not as top-level named properties. The top-level emailAddress and firstName fields are convenience shortcuts that may not be present in all API depth levels.

Solution: Use depth=complete on contact queries and inspect the fieldValues array to find the field ID corresponding to each attribute. Build a field ID lookup map in your transformer to extract values by ID. Alternatively, use depth=complete and rely on the top-level convenience fields (emailAddress, firstName, lastName) which are populated in complete depth responses.

```
// Safe field extraction from fieldValues array
const getField = (contact, fieldId) => {
  const field = (contact.fieldValues || []).find(f => f.id === String(fieldId));
  return field ? field.value : 'N/A';
};
// Usage: getField(contact, 100) where 100 is your email address field ID
```

### Bulk API export stays in 'pending' status indefinitely

Cause: The export sync was not started after creating the export definition. Eloqua's Bulk API requires two steps: creating the export definition, then creating a sync record to actually trigger the export process.

Solution: After POST-ing the export definition and receiving an export URI in the response, make a second POST request to /syncs with body { "syncedInstanceUri": "the_export_uri" } to start the sync. Then poll GET /syncs/{sync_id} until the status field is 'success' or 'error'. Only after 'success' should you GET the data from the export's data endpoint.

## Frequently asked questions

### What is an Eloqua 'pod' and why does it affect the API base URL?

Eloqua hosts customer data on geographically and logically distinct server clusters called 'pods' (e.g., secure.eloqua.com, secure2.eloqua.com, secure3.eloqua.com). When you sign up for Eloqua, your account is assigned to a specific pod. All API calls must go to your account's pod — calling a different pod URL returns authentication errors even with valid credentials. The pod discovery endpoint at login.eloqua.com/id tells you which pod your account uses.

### Should I use Basic Auth or OAuth 2.0 for the Eloqua API connection?

For production enterprise deployments, OAuth 2.0 is recommended because it avoids storing a user password in Retool and supports token expiration and refresh. Basic Auth is simpler to set up and suitable for internal tools where a dedicated API user account is configured with a long-lived password. If your Eloqua instance enforces SSO or MFA for all users, OAuth 2.0 is required as Basic Auth will not work for MFA-protected accounts.

### Can I query Eloqua custom objects from Retool?

Yes. Eloqua custom objects have their own API endpoints under /customObjects/{custom_object_id}/data. First retrieve the list of custom objects with GET /assets/customObjects to find the ID and field structure of the custom object you want to query. Then use GET /customObjects/{id}/data with filter, count, and page parameters to retrieve records. The same transformer pattern applies — map the elements array to flat rows using the field names defined in the custom object schema.

### How do I handle Eloqua's API rate limits in Retool?

Eloqua's REST API has rate limits that vary by instance type — typically around 2 requests per second for REST API calls. For Retool dashboards with multiple queries, add debounce settings on search inputs and filter controls to prevent rapid successive API calls. Enable query caching (Advanced tab → Cache results) for analytics queries with a 5-10 minute TTL. For bulk operations, use the Eloqua Bulk API via Retool Workflows, which has different (higher) rate limits than the standard REST API.

### Can I connect both Eloqua and Salesforce to the same Retool app?

Yes. Create separate REST API Resources for Eloqua and Salesforce. In your Retool app, build queries from both resources and join the data in a JavaScript transformer using a common field like email address or Salesforce Account ID. This is one of the most common enterprise Retool patterns — a contact view that shows Eloqua engagement history alongside Salesforce opportunity status, giving RevOps teams a full pipeline picture without switching between systems.

---

Source: https://www.rapidevelopers.com/retool-integrations/eloqua
© RapidDev — https://www.rapidevelopers.com/retool-integrations/eloqua
