> ## Documentation Index
> Fetch the complete documentation index at: https://docs.base44.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload Google Ads conversions

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Reports conversions to Google Ads from your own server, for the conversions only your server knows about.

This is the counterpart to the browser-side `gtag('event', 'conversion', ...)` call. Use it for a payment that actually settled, an order confirmed after review, or anything an ad blocker would stop the browser reporting. Each conversion needs the goal it counts towards, when it happened, and the click it came from.

Read the outcome from the counts rather than from `status`. A partial batch still comes back as `success`, so compare `total_uploaded` with `total_submitted`. The difference is accounted for by `duplicates_removed`, `cross_request_duplicates`, and `skipped_no_click_id`, and anything left over is rows Google rejected one by one. Google does not tell Base44 which rows those were, so the response cannot name them.

Send `order_id` on every conversion you can. Base44 remembers each conversion it has reported and never reports the same one twice, keyed on the order or click ID together with the goal and the timestamp, and that record does not expire. Sending a corrected value for a conversion you already reported has no effect for that reason. Use [Upload enhanced conversions](/api-reference/upload-enhanced-google-ads-conversions) to add buyer details to a conversion you have already reported.

<Note>There is no cap on how many rows you can send and no rate limit on this endpoint. Base44 sends them to Google in batches of 2,000, one batch after another, while your request stays open, so a very large list means a very long request. Keep each call to a few thousand rows and send several calls instead.</Note>

<Note>A 409 means the request to Google timed out after it was sent, so those conversions may or may not have landed. Retrying is safe. Base44 has already recorded them as sent, so the retry reports them under `cross_request_duplicates` rather than counting them twice.</Note>

<Note>This endpoint accepts a personal API key. Workspace API keys are not authorized for it and are rejected with a 403.</Note>



## OpenAPI

````yaml /developers/references/app-management/app-management-openapi.json post /api/apps/{app_id}/google-ads/conversions/upload
openapi: 3.1.0
info:
  title: Base44 App Management API
  version: 1.0.0
servers:
  - url: https://app.base44.com
security:
  - ApiKeyAuth: []
paths:
  /api/apps/{app_id}/google-ads/conversions/upload:
    post:
      summary: Upload Google Ads conversions
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Reports conversions to Google Ads from your own server, for the
        conversions only your server knows about.


        This is the counterpart to the browser-side `gtag('event', 'conversion',
        ...)` call. Use it for a payment that actually settled, an order
        confirmed after review, or anything an ad blocker would stop the browser
        reporting. Each conversion needs the goal it counts towards, when it
        happened, and the click it came from.


        Read the outcome from the counts rather than from `status`. A partial
        batch still comes back as `success`, so compare `total_uploaded` with
        `total_submitted`. The difference is accounted for by
        `duplicates_removed`, `cross_request_duplicates`, and
        `skipped_no_click_id`, and anything left over is rows Google rejected
        one by one. Google does not tell Base44 which rows those were, so the
        response cannot name them.


        Send `order_id` on every conversion you can. Base44 remembers each
        conversion it has reported and never reports the same one twice, keyed
        on the order or click ID together with the goal and the timestamp, and
        that record does not expire. Sending a corrected value for a conversion
        you already reported has no effect for that reason. Use [Upload enhanced
        conversions](/api-reference/upload-enhanced-google-ads-conversions) to
        add buyer details to a conversion you have already reported.


        <Note>There is no cap on how many rows you can send and no rate limit on
        this endpoint. Base44 sends them to Google in batches of 2,000, one
        batch after another, while your request stays open, so a very large list
        means a very long request. Keep each call to a few thousand rows and
        send several calls instead.</Note>


        <Note>A 409 means the request to Google timed out after it was sent, so
        those conversions may or may not have landed. Retrying is safe. Base44
        has already recorded them as sent, so the retry reports them under
        `cross_request_duplicates` rather than counting them twice.</Note>


        <Note>This endpoint accepts a personal API key. Workspace API keys are
        not authorized for it and are rejected with a 403.</Note>
      operationId: upload_conversions_api_apps__app_id__google_ads_conversions_upload_post
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
            description: >-
              ID of the app whose Google Ads conversion tracking you want to
              manage.
            title: App Id
          description: >-
            ID of the app whose Google Ads conversion tracking you want to
            manage.
          example: 6820f3a4e7b91d003c45a1f2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              title: UploadConversions
              type: object
              properties:
                conversions:
                  type: array
                  description: >-
                    The conversions to report. An empty list is accepted and
                    reports nothing.
                  items:
                    type: object
                    required:
                      - conversion_action_id
                      - conversion_date_time
                    properties:
                      conversion_action_id:
                        type: string
                        description: >-
                          Google Ads' own ID for the goal this conversion counts
                          towards, as returned in `google_action_id` by [List
                          conversion
                          actions](/api-reference/list-google-ads-conversion-actions).
                        example: '7654321'
                      conversion_date_time:
                        type: string
                        description: >-
                          When the conversion happened, as `YYYY-MM-DD
                          HH:MM:SS+HH:MM` including the offset. Google rejects
                          any other format with a 400, and Base44 passes the
                          value through unchanged.
                        example: '2026-08-25 14:05:00+00:00'
                      gclid:
                        type: string
                        description: >-
                          The Google click ID the conversion is attributed to,
                          taken from the `gclid` query parameter on the landing
                          URL.
                        example: Cj0KCQjw1...
                      gbraid:
                        type: string
                        description: >-
                          The click ID for an iOS app-to-web journey, used
                          instead of `gclid` when the landing URL carries
                          `gbraid`.
                        example: 0AAAAAo1bC2d3E4f
                      wbraid:
                        type: string
                        description: >-
                          The click ID for a web-to-app journey, used instead of
                          `gclid` when the landing URL carries `wbraid`.
                        example: CjwKCAj0bC2d3E4f
                      conversion_value:
                        type: number
                        description: >-
                          What the conversion was worth, in the currency given
                          by `currency_code`. It must be zero or more.
                        default: 0
                        example: 89.9
                      currency_code:
                        type: string
                        description: >-
                          Currency of `conversion_value` as a three-letter ISO
                          4217 code. Base44 uppercases it before sending, so
                          `eur` and `EUR` both work.
                        default: USD
                        example: EUR
                      order_id:
                        type: string
                        description: >-
                          Your own ID for the order behind the conversion. Send
                          it whenever you have one, because it is what lets
                          Google dedupe the conversion on its side as well.
                        example: ORD-10482
                      user_identifiers:
                        type: object
                        description: >-
                          Hashed details of the buyer, which improve how well
                          Google matches the conversion to a click. Hash every
                          value yourself, because Base44 sends them as given.
                        properties:
                          hashed_email:
                            type: string
                            description: >-
                              The buyer's email address, lowercased, trimmed,
                              and SHA-256 hashed to lowercase hex.
                            example: >-
                              d5b8e2b9a1c4f60e7a2f3c8d91b45e6f70a1c2d3e4f5061728394a5b6c7d8e9f
                          hashed_phone_number:
                            type: string
                            description: >-
                              The buyer's phone number in E.164 form, SHA-256
                              hashed to lowercase hex.
                            example: >-
                              9f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0
                          hashed_first_name:
                            type: string
                            description: >-
                              The buyer's first name, lowercased and SHA-256
                              hashed to lowercase hex. Base44 only sends it
                              together with `hashed_last_name`, so one without
                              the other is dropped and has no effect.
                            example: >-
                              3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d
                          hashed_last_name:
                            type: string
                            description: >-
                              The buyer's last name, lowercased and SHA-256
                              hashed to lowercase hex. Send it together with
                              `hashed_first_name`, because one without the other
                              is dropped.
                            example: >-
                              706f5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a392817
                  example:
                    - conversion_action_id: '7654321'
                      conversion_date_time: '2026-08-25 14:05:00+00:00'
                      gclid: Cj0KCQjw1...
                      conversion_value: 89.9
                      currency_code: EUR
                      order_id: ORD-10482
            example:
              conversions:
                - conversion_action_id: '7654321'
                  conversion_date_time: '2026-08-25 14:05:00+00:00'
                  gclid: Cj0KCQjw1...
                  conversion_value: 89.9
                  currency_code: EUR
                  order_id: ORD-10482
      responses:
        '200':
          description: What happened to each conversion in the batch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversionUploadResult'
        '400':
          description: >-
            Google Ads rejected the whole request, for example a malformed
            `conversion_date_time` or a `conversion_action_id` that is not on
            the account. The response message carries Google's reason.
        '401':
          description: Missing or invalid credentials.
        '403':
          description: >-
            You don't have access to this app, the app does not exist, or you
            used a workspace API key. A missing app and an app you cannot reach
            are deliberately the same answer.
        '404':
          description: The app has no connected Google Ads account.
        '409':
          description: >-
            The request to Google Ads timed out after being sent, so the
            conversions may or may not have landed. Retrying is safe and reports
            them as cross-request duplicates.
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: Google Ads is rate limiting the account. Retry later.
components:
  schemas:
    ConversionUploadResult:
      properties:
        status:
          type: string
          title: Status
          description: >-
            Whether anything reached Google. The value is `success` when at
            least one conversion landed and `no_rows_uploaded` when none did, so
            it does not tell you the whole batch landed.
          example: success
        total_submitted:
          type: integer
          title: Total Submitted
          description: >-
            How many conversions you sent, counted before any of the checks
            below.
          example: 12
        duplicates_removed:
          type: integer
          title: Duplicates Removed
          description: >-
            How many conversions were dropped as duplicates of another
            conversion in the same request, matching on order or click ID
            together with the goal and the timestamp.
          example: 1
        cross_request_duplicates:
          type: integer
          title: Cross Request Duplicates
          description: >-
            How many conversions a previous request already reported. Base44
            remembers every conversion it has sent, permanently, so a retried
            upload lands here instead of counting twice.
          example: 2
        skipped_no_click_id:
          type: integer
          title: Skipped No Click Id
          description: >-
            How many conversions were dropped before Google saw them because
            they carried none of `gclid`, `gbraid`, or `wbraid`. Google requires
            one of the three.
          example: 1
        total_uploaded:
          type: integer
          title: Total Uploaded
          description: How many conversions Google accepted.
          example: 8
      type: object
      required:
        - status
        - total_submitted
        - duplicates_removed
        - cross_request_duplicates
        - skipped_no_click_id
        - total_uploaded
      title: ConversionUploadResult
      description: What happened to a batch of uploaded conversions.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api_key
      description: Personal API key.

````