# How to Integrate Lovable with X (Twitter) Ads

- Tool: Lovable
- Difficulty: Advanced
- Time required: 60 minutes
- Last updated: March 2026

## TL;DR

To integrate Lovable with X (Twitter) Ads, create a Supabase Edge Function that signs requests using OAuth 1.0a — the mandatory signing mechanism for Twitter's Ads API. Store your consumer key, consumer secret, access token, and access token secret in Cloud → Secrets. Use Lovable's chat to build X Ads campaign dashboards, promoted tweet managers, and conversation analytics tools.

## Build X (Twitter) Ads campaign dashboards with OAuth 1.0a signing in Lovable

The X Ads API (formerly Twitter Ads) is the most technically demanding of the major social advertising APIs to integrate. While most modern advertising APIs (TikTok, LinkedIn, Pinterest) use standard OAuth 2.0 Bearer tokens, X's Ads API requires OAuth 1.0a — a signing mechanism where every individual request is signed with a cryptographic HMAC-SHA1 signature computed from the request URL, parameters, HTTP method, and your credentials. There is no way to generate this signature client-side without exposing your credentials, making the Edge Function proxy absolutely non-negotiable for this integration.

The X Ads API's structure follows X's three-tier advertising hierarchy: accounts (the advertiser account), campaigns (with objective and budget), line items (ad groups with targeting and bid settings), and promoted tweets (the specific tweet content being promoted). This maps to the typical advertising platform hierarchy but with X-specific terminology.

X (formerly Twitter) advertising is most effective for brands where conversation is central to the value: B2C brands that engage in real-time cultural conversations, news and media organizations, political campaigns, sports brands, and technology companies whose audience is active on X. Unlike LinkedIn (professional targeting) or Pinterest (visual discovery), X's advertising reaches consumers who are engaged in public discourse and trending topics. The targeting options reflect this: interest categories, keyword targeting, follower lookalikes, and event targeting. Due to X's multiple API version changes following its acquisition in 2022 and rebrand from Twitter, verify endpoint URLs against current X Developer documentation before implementing.

## Before you start

- A Lovable account with a project that has Lovable Cloud enabled
- An X Developer account with Elevated or higher access — apply at developer.twitter.com
- An X Ads account with active campaigns and Ads API access enabled
- OAuth 1.0a credentials: consumer key, consumer secret, access token, and access token secret
- Your X Ads account ID — the numeric string in the ads.twitter.com URL

## Step-by-step guide

### 1. Set up X Developer credentials with Ads API access

Navigate to developer.twitter.com and sign in with your X account. If you do not have a developer account, click 'Apply for a developer account' and go through the application process. X requires Basic or Elevated access for API usage; Ads API access specifically requires Elevated access or a partner agreement.

Once your developer account is approved, go to the Developer Portal and create a project and app. Under your app's settings, navigate to 'Keys and tokens'. You need four credential values:

1. API Key (also called Consumer Key)
2. API Key Secret (also called Consumer Secret)
3. Access Token
4. Access Token Secret

The API Key and Secret are generated when you create the app. The Access Token and Secret are generated by clicking 'Generate' in the app's authentication settings — these represent your own account's authorization. For multi-user apps where other X users connect their accounts, you would implement the OAuth 1.0a three-legged flow, but for a single-account ads dashboard, your own access token is sufficient.

For Ads API access, go to developer.twitter.com/en/portal/projects/{your-project}/apps/{your-app}/auth-settings and verify the app has Read, Write, and Direct Messages permissions. You also need to apply for Ads API access at developer.twitter.com/en/docs/twitter-ads-api — this may require a separate application review for your specific use case.

Find your Ads account ID by logging into ads.twitter.com — it is the numeric string in the URL when viewing your campaigns.

**Expected result:** You have an X Developer app with four OAuth 1.0a credentials: API Key, API Key Secret, Access Token, and Access Token Secret. Your developer account has Elevated access and Ads API access approved.

### 2. Store X Ads credentials in Cloud → Secrets

Open your Lovable project and navigate to Cloud → Secrets via the '+' icon next to Preview. Add the following five secrets:

- Name: X_CONSUMER_KEY — Value: your API Key (Consumer Key)
- Name: X_CONSUMER_SECRET — Value: your API Key Secret (Consumer Secret)
- Name: X_ACCESS_TOKEN — Value: your Access Token
- Name: X_ACCESS_TOKEN_SECRET — Value: your Access Token Secret
- Name: X_ADS_ACCOUNT_ID — Value: your numeric X Ads account ID

All five are required. OAuth 1.0a signing requires all four credential values to compute the HMAC-SHA1 signature on every request — unlike OAuth 2.0 where only the access token is needed for API calls. The signing happens in the Edge Function using Deno's Web Crypto API.

X advertising credentials authorize access to campaign management and ad spend data. Lovable's security infrastructure blocks approximately 1,200 hardcoded API keys per day, but these credentials specifically require extra care — never paste them in Lovable's chat or any visible code.

**Expected result:** All five X Ads secrets — X_CONSUMER_KEY, X_CONSUMER_SECRET, X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET, and X_ADS_ACCOUNT_ID — appear in Cloud → Secrets with masked values.

### 3. Implement OAuth 1.0a signing and create the Edge Function

The core challenge of the X Ads integration is implementing OAuth 1.0a request signing. Unlike OAuth 2.0 where you simply add an Authorization: Bearer {token} header, OAuth 1.0a requires you to:

1. Collect the request URL, HTTP method, and all query/body parameters
2. Add OAuth-specific parameters (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_token, oauth_version)
3. Percent-encode and sort all parameters alphabetically
4. Build the signature base string from the HTTP method, encoded URL, and encoded parameters
5. Compute the HMAC-SHA1 signature using consumer_secret&access_token_secret as the signing key
6. Build the Authorization header with all OAuth parameters and the signature

Deno's Web Crypto API provides the HMAC-SHA1 implementation needed for this signing. The code below implements the full OAuth 1.0a signing process — paste it into Lovable's chat with the prompt below to generate the full Edge Function.

The X Ads API base URL is https://ads-api.twitter.com/12/ (verify the version number against current documentation). Key endpoints: GET /accounts/{id}/campaigns for campaign list, GET /accounts/{id}/stats/async/query for analytics, GET /accounts/{id}/line_items for line items.

```
// supabase/functions/twitter-ads/index.ts
// OAuth 1.0a signing for X Ads API

function percentEncode(str: string): string {
  return encodeURIComponent(str).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
}

async function sign(method: string, url: string, params: Record<string, string>, consumerKey: string, consumerSecret: string, accessToken: string, tokenSecret: string): Promise<string> {
  const oauthParams: Record<string, string> = {
    oauth_consumer_key: consumerKey,
    oauth_nonce: crypto.randomUUID().replace(/-/g, ''),
    oauth_signature_method: 'HMAC-SHA1',
    oauth_timestamp: Math.floor(Date.now() / 1000).toString(),
    oauth_token: accessToken,
    oauth_version: '1.0',
    ...params,
  };

  const sortedParams = Object.keys(oauthParams)
    .sort()
    .map((k) => `${percentEncode(k)}=${percentEncode(oauthParams[k])}`)
    .join('&');

  const baseString = [method.toUpperCase(), percentEncode(url), percentEncode(sortedParams)].join('&');
  const signingKey = `${percentEncode(consumerSecret)}&${percentEncode(tokenSecret)}`;

  const key = await crypto.subtle.importKey(
    'raw',
    new TextEncoder().encode(signingKey),
    { name: 'HMAC', hash: 'SHA-1' },
    false,
    ['sign']
  );
  const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(baseString));
  const signatureB64 = btoa(String.fromCharCode(...new Uint8Array(signature)));

  const authHeader = 'OAuth ' + Object.keys(oauthParams)
    .filter((k) => k.startsWith('oauth_'))
    .sort()
    .map((k) => `${percentEncode(k)}="${percentEncode(oauthParams[k])}"`)
    .join(', ') + `, oauth_signature="${percentEncode(signatureB64)}"`;

  return authHeader;
}

Deno.serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      },
    });
  }

  const consumerKey = Deno.env.get('X_CONSUMER_KEY')!;
  const consumerSecret = Deno.env.get('X_CONSUMER_SECRET')!;
  const accessToken = Deno.env.get('X_ACCESS_TOKEN')!;
  const tokenSecret = Deno.env.get('X_ACCESS_TOKEN_SECRET')!;
  const accountId = Deno.env.get('X_ADS_ACCOUNT_ID')!;

  if (!consumerKey || !consumerSecret || !accessToken || !tokenSecret) {
    return new Response(JSON.stringify({ error: 'X Ads credentials not fully configured' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },
    });
  }

  const { action, params } = await req.json();
  let url: string;

  if (action === 'campaigns') {
    url = `https://ads-api.twitter.com/12/accounts/${accountId}/campaigns`;
  } else if (action === 'account') {
    url = `https://ads-api.twitter.com/12/accounts/${accountId}`;
  } else if (action === 'line_items') {
    url = `https://ads-api.twitter.com/12/accounts/${accountId}/line_items${params?.campaign_id ? `?campaign_ids=${params.campaign_id}` : ''}`;
  } else {
    return new Response(JSON.stringify({ error: 'Invalid action' }), {
      status: 400,
      headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },
    });
  }

  const authHeader = await sign('GET', url.split('?')[0], {}, consumerKey, consumerSecret, accessToken, tokenSecret);

  const response = await fetch(url, {
    method: 'GET',
    headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' },
  });

  const data = await response.json();
  return new Response(JSON.stringify(data), {
    status: response.ok ? 200 : response.status,
    headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },
  });
});
```

**Expected result:** The twitter-ads Edge Function is deployed with OAuth 1.0a signing. Calling it with { action: 'account' } returns your X Ads account information. Calling { action: 'campaigns' } returns your campaigns.

### 4. Build the X Ads campaign dashboard

With the OAuth 1.0a signing working, use Lovable's chat to build the campaign management dashboard. X Ads analytics are retrieved through an asynchronous stats query system — you POST a stats request, receive a job ID, then poll the job status until results are available. This asynchronous pattern is more complex than synchronous analytics endpoints used by most other ad platforms.

For the campaign overview, fetch campaigns synchronously (the /campaigns endpoint is synchronous) and display the core campaign attributes: name, status, funding_instrument_id, daily_budget_amount_local_micro (divided by 1,000,000 to get dollar amounts), entity_status, and start/end times.

For performance metrics, use the synchronous /stats endpoint for entity-level stats (impressions, engagements, spend) with a date range and the correct metric groups. X Ads metrics are returned in the 'organic_metrics' and 'promoted_metrics' groups — promoted_metrics covers paid amplification while organic_metrics covers unpromoted engagement.

For promoted tweet creative performance, fetch line items and then the promoted tweets for each line item. This shows which specific tweet content is being amplified, its engagement rate, and whether it warrants continuation or pausing.

**Expected result:** An X Ads dashboard shows campaigns with their status and budget data. Budget amounts are displayed in dollar format after dividing the micro-cent values by 1,000,000. The details row shows line items for each campaign.

## Best practices

- Store all four OAuth 1.0a credentials in Cloud → Secrets — the signing process requires all four values server-side and any single credential exposure compromises the entire signing chain
- Implement thorough percent-encoding in the OAuth signature function — even a single character encoded differently from X's specification will produce an invalid signature
- Convert X Ads monetary values from micro-cents to display currency by dividing by 1,000,000 before showing to users
- Cache campaign and line item data in Supabase — X Ads API has rate limits and the synchronous campaign/line-item endpoints are more reliable to cache than the asynchronous stats endpoints
- Test the OAuth signing implementation with the simple account endpoint before adding more complex endpoints — a valid 200 response from /accounts/{id} confirms the entire signing chain is working
- Verify X Ads API endpoint version numbers against current documentation — X has changed API versions and deprecated endpoints multiple times since the Twitter acquisition and rebrand
- Handle micro-currency values consistently throughout the application — define a single conversion function and use it everywhere rather than performing the division inline in multiple components

## Use cases

### X Ads campaign performance dashboard

Build a campaign analytics dashboard showing X Ads performance metrics: impressions, engagements, engagement rate, clicks, spend, and CPE (cost per engagement) across all active campaigns. The dashboard lets marketers monitor campaign health and compare performance across different creative executions.

Prompt example:

```
Create an X Ads dashboard that calls the twitter-ads Edge Function to fetch all campaigns for my account. Show a table with campaign name, funding instrument, daily budget, total spend, impressions, engagements, engagement rate, and status. Add a date range picker and campaign objective filter. Show a line chart of daily spend vs. engagement over the last 30 days.
```

### Promoted tweet manager with creative comparison

Build a tool for managing promoted tweet content that shows how different tweet creative executions perform head-to-head. The dashboard shows per-tweet metrics for promoted content, helping ad managers identify high-performing organic tweets worth amplifying and underperforming paid content to pause.

Prompt example:

```
Build a promoted tweet performance page that calls the twitter-ads Edge Function to fetch promoted tweets for my current campaigns. Show each tweet with its text preview, promotion status, impressions, engagements, engagement rate, URL clicks, and spend. Add a 'Pause Promotion' button for each tweet. Sort by engagement rate descending to show the best performers first.
```

### Real-time campaign budget monitoring and alerts

Create a budget pacing monitor that checks X Ads campaign spend against daily and lifetime budget targets, flags campaigns that are over- or under-spending, and stores alerts in Supabase. This helps marketers catch budget issues before significant overspend occurs on campaigns.

Prompt example:

```
Build a budget monitoring dashboard that calls the twitter-ads Edge Function to fetch today's spend for all active campaigns. Calculate the expected daily spend from the campaign's daily budget and compare. Show a traffic light indicator (green/yellow/red) for each campaign based on pacing — green if within 20% of expected, yellow if 20-50% off, red if over 50% off target. Store alerts in a Supabase table.
```

## Troubleshooting

### API returns 401 Unauthorized with 'Invalid or expired token' or 'Could not authenticate you'

Cause: The OAuth 1.0a signature is invalid. Common causes: incorrect parameter sorting, wrong percent-encoding, clock skew (OAuth timestamps must be within 5 minutes of server time), or credentials stored with extra whitespace.

Solution: Verify all four credential values in Cloud → Secrets have no leading or trailing spaces. Check that the timestamp in the signing function uses the current UTC time — Deno's Math.floor(Date.now() / 1000) is correct. Log the signature base string and verify the sorting order. Test with the account action (simplest endpoint) before testing more complex ones.

### API returns 403 Forbidden with 'Your credentials do not allow access to this resource'

Cause: Your X Developer account does not have Elevated access or your app has not been approved for X Ads API access specifically.

Solution: Go to developer.twitter.com and check your app's access level. The Ads API requires Elevated access at minimum. If you have Basic access, apply for Elevated access with your advertising use case. Additionally, verify your app has been specifically approved for the Ads API — some apps have Elevated general API access but not Ads API access.

### Budget amounts in the campaigns response appear as very large numbers like 1000000

Cause: X Ads API returns monetary values in micro-cents (millionths of the local currency unit). A daily budget of $10.00 is stored as 10000000.

Solution: Divide all monetary values from X Ads API by 1,000,000 to get the actual currency amount. For display: (campaign.daily_budget_amount_local_micro / 1000000).toFixed(2). Apply this conversion everywhere monetary values appear — daily budgets, total spend, CPE, CPM.

```
// Convert X Ads micro-currency to display currency
const formatBudget = (microCents: number): string => {
  return `$${(microCents / 1_000_000).toFixed(2)}`;
};
```

### OAuth nonce conflicts or 'Nonce already used' errors

Cause: The same OAuth nonce value was used in two requests within the nonce validity window. This can happen if the Edge Function is called rapidly with the same timestamp and random generation produces a collision.

Solution: Ensure the nonce generation in the sign() function produces sufficiently unique values. Using crypto.randomUUID().replace(/-/g, '') produces a 32-character hex string with very low collision probability. Avoid using simple timestamp-based nonces — they are more likely to collide under rapid API usage.

## Frequently asked questions

### Why does X Ads API still use OAuth 1.0a instead of OAuth 2.0?

X's advertising API predates the widespread adoption of OAuth 2.0 and has not been updated to use the newer standard. OAuth 1.0a provides strong security through per-request signing (making replayed requests invalid) but is significantly more complex to implement than OAuth 2.0's Bearer token approach. X has offered OAuth 2.0 Bearer tokens for some of its newer API endpoints (v2 read-only access) but the Ads API specifically remains on OAuth 1.0a as of March 2026.

### What is the difference between organic and promoted metrics in X Ads?

In X's advertising model, a tweet can receive both organic engagement (from users who see it naturally in their feed because they follow you or it was recommended) and promoted engagement (from users who see it as a paid ad). The API returns these separately: organic_metrics covers natural engagement on the tweet itself, and promoted_metrics covers engagement generated through paid promotion. For advertising ROI analysis, use promoted_metrics. For understanding total tweet performance including organic reach, sum both.

### How do X Ads account IDs work?

X Ads account IDs are alphanumeric strings (not purely numeric like some other ad platforms) visible in the ads.twitter.com URL. They appear in the URL as /accounts/abc123. This account ID is required for all Ads API calls and is different from your X user ID or @username. One X account can have multiple Ads accounts, so if you are building for multiple clients, you need to handle multiple account IDs.

### What happened to the Twitter Ads API after Elon Musk's acquisition?

Following the acquisition and rebranding from Twitter to X in 2022-2023, the Ads API has gone through multiple changes including endpoint version updates, access tier restructuring, and some endpoint deprecations. The core OAuth 1.0a signing requirement and advertising hierarchy (accounts, campaigns, line items, promoted tweets) remain intact. Always check developer.twitter.com for the current API version and endpoint documentation — endpoint URLs in older tutorials may reference deprecated versions. As of March 2026, v12 is the current Ads API version.

---

Source: https://www.rapidevelopers.com/lovable-integration/twitter-ads
© RapidDev — https://www.rapidevelopers.com/lovable-integration/twitter-ads
