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

# Upload Attachments

> Upload documentation attachments for applications (invoices, permits, photos, etc.). This covers the two-step upload process: creating an upload URL and committing the upload.

## Overview

Upload documentation attachments for applications using a three-step process:

1. **Create Upload URL**: Request a signed URL
2. **Upload File**: PUT file to the signed URL
3. **Commit Upload**: Finalize the attachment

<Note>
  * `application_id` comes from the **query parameter**, not the request body
  * The signed upload URL expires quickly (typically 60 seconds)
  * The `path` in commit must match the path from create upload URL response
</Note>

<Warning>
  Make sure to upload and commit the file before the signed URL expires, or request a new upload URL.
</Warning>

For detailed workflow explanation, storage paths, attachment types, and best practices, see the [Attachments Workflow section](/api-reference/guides/api-guide#attachments-workflow) in the API Guide.


## OpenAPI

````yaml POST /applications/attachments
openapi: 3.0.3
info:
  title: Leap Energy API
  description: >-
    The Leap Energy API provides a unified interface for managing rebate
    incentives and applications.


    ## Incentives API

    Search for and calculate incentive amounts across multiple utility programs.
    Given a customer

    address and building type, the API identifies applicable utility programs
    and calculates potential

    incentive amounts for various device and tier combinations. When your
    organization has a webhook URL

    configured (Webhook Config API), each successful lookup triggers a POST to
    your URL with the response

    and optional custom fields (`webhook_custom_fields`).


    ## Applications API

    Manage rebate applications on behalf of customers. Applications represent a
    customer's request

    for a rebate under a specific utility program, including customer
    information, device details,

    and application metadata.
  version: 2.0.0
  contact:
    name: Leap Energy
    url: https://leap.energy
servers:
  - url: https://api.incentives.leap.energy/alpha
    description: Production API
security:
  - ApiKeyAuth: []
  - BearerAuth: []
paths:
  /applications/attachments:
    post:
      summary: Create signed upload URL for attachment
      description: >-
        Creates a signed upload URL for uploading an attachment to the
        application. After receiving the signed URL, upload the file using a PUT
        request, then commit using the commit endpoint.


        **Authentication:**

        Requires API key authentication via `x-api-key` header. The application
        must belong to your organization.


        **Application ID:**

        Use the `application_id` query parameter to identify which application
        to attach the file to. The API verifies the application belongs to your
        organization.
      operationId: createAttachmentUploadUrl
      parameters:
        - name: application_id
          in: query
          description: >-
            Application ID to attach the file to. The application must belong to
            your organization.
          required: true
          schema:
            type: integer
          example: 123
      requestBody:
        required: true
        description: >-
          Attachment metadata for creating the upload URL. Note:
          `application_id` comes from query parameter, not body.
        content:
          application/json:
            schema:
              type: object
              required:
                - attachment_type
                - filename
                - contentType
              properties:
                attachment_type:
                  type: string
                  enum:
                    - INVOICE_EQUIPMENT
                    - INVOICE_INSTALL
                    - PERMIT
                    - PHOTO_NAMEPLATE
                    - PHOTO_INSTALL
                    - PHOTO_METER
                    - AHRI_CERT
                    - NEAT_REPORT
                    - INCOME_FORM
                    - W9
                    - OTHER
                    - MODEL_TECH_SPECS
                    - VEHICLE_PURCHASE
                    - UTILITY_BILL
                    - VEHICLE_REGISTRATION
                    - TARIFF_CONFIRMATION
                  description: Type of attachment being uploaded
                  example: INVOICE_EQUIPMENT
                filename:
                  type: string
                  description: Name of the file being uploaded
                  example: invoice.pdf
                contentType:
                  type: string
                  description: MIME type of the file
                  example: application/pdf
      responses:
        '200':
          description: Successfully created upload URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    example: true
                  requestId:
                    type: string
                    example: b2c3d4e5-f6a7-8901-bcde-f12345678901
                  uploadUrl:
                    type: string
                    format: uri
                    description: Signed URL for uploading the file (PUT file bytes here)
                    example: >-
                      https://your-project.supabase.co/storage/v1/object/sign/attachments/app-123/uuid-invoice.pdf?token=...
                  path:
                    type: string
                    description: Object path to use in commit step
                    example: app-123/a1b2c3d4-e5f6-7890-abcd-ef1234567890-invoice.pdf
                  bucket:
                    type: string
                    example: attachments
                  instructions:
                    type: string
                    example: >-
                      PUT bytes to uploadUrl with the file content-type, then
                      call POST /applications/attachments/commit to finalize.
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST \
              "https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123" \
              -H "x-api-key: leap_live_..." \
              -H "Content-Type: application/json" \
              -d '{
                "attachment_type": "INVOICE_EQUIPMENT",
                "filename": "invoice.pdf",
                "contentType": "application/pdf"
              }'
        - lang: python
          label: Python
          source: >-
            import requests


            url =
            "https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123"


            payload = {
                "attachment_type": "INVOICE_EQUIPMENT",
                "filename": "invoice.pdf",
                "contentType": "application/pdf"
            }


            headers = {
                "x-api-key": "leap_live_...",
                "Content-Type": "application/json"
            }


            response = requests.post(url, json=payload, headers=headers)

            print(response.json())
        - lang: javascript
          label: JavaScript
          source: |-
            const response = await fetch(
              'https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123',
              {
                method: 'POST',
                headers: {
                  'x-api-key': 'leap_live_...',
                  'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                  attachment_type: 'INVOICE_EQUIPMENT',
                  filename: 'invoice.pdf',
                  contentType: 'application/pdf'
                })
              }
            );

            const data = await response.json();
            console.log(data);
        - lang: php
          label: PHP
          source: >-
            <?php


            $url =
            'https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123';


            $payload = [
                'attachment_type' => 'INVOICE_EQUIPMENT',
                'filename' => 'invoice.pdf',
                'contentType' => 'application/pdf'
            ];


            $ch = curl_init($url);

            curl_setopt($ch, CURLOPT_POST, true);

            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'x-api-key: leap_live_...',
                'Content-Type: application/json'
            ]);

            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);


            $response = curl_exec($ch);

            curl_close($ch);


            print_r(json_decode($response, true));
        - lang: java
          label: Java
          source: >-
            import java.net.http.*;

            import java.net.URI;


            HttpClient client = HttpClient.newHttpClient();


            String json = """{
              "attachment_type": "INVOICE_EQUIPMENT",
              "filename": "invoice.pdf",
              "contentType": "application/pdf"
            }""";


            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123"))
                .header("x-api-key", "leap_live_...")
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(json))
                .build();

            HttpResponse<String> response = client.send(request,
            HttpResponse.BodyHandlers.ofString());

            System.out.println(response.body());
        - lang: ruby
          label: Ruby
          source: >-
            require 'net/http'

            require 'json'


            uri =
            URI('https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123')


            payload = {
              attachment_type: 'INVOICE_EQUIPMENT',
              filename: 'invoice.pdf',
              contentType: 'application/pdf'
            }


            http = Net::HTTP.new(uri.host, uri.port)

            http.use_ssl = true


            request = Net::HTTP::Post.new(uri)

            request['x-api-key'] = 'leap_live_...'

            request['Content-Type'] = 'application/json'

            request.body = payload.to_json


            response = http.request(request)

            puts JSON.parse(response.body)
        - lang: go
          label: Go
          source: "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc main() {\n\turl := \"https://api.incentives.leap.energy/alpha/applications/attachments?application_id=123\"\n\n\tpayload := map[string]interface{}{\n\t\t\"attachment_type\": \"INVOICE_EQUIPMENT\",\n\t\t\"filename\":        \"invoice.pdf\",\n\t\t\"contentType\":     \"application/pdf\",\n\t}\n\n\tjsonData, _ := json.Marshal(payload)\n\treq, _ := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\treq.Header.Set(\"x-api-key\", \"leap_live_...\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, _ := client.Do(req)\n\tdefer resp.Body.Close()\n\n\tvar result map[string]interface{}\n\tjson.NewDecoder(resp.Body).Decode(&result)\n\tfmt.Println(result)\n}"
components:
  schemas:
    Error:
      type: object
      required:
        - code
        - message
        - requestId
      properties:
        code:
          type: string
          description: Error code identifying the type of error
          example: not_found
        message:
          type: string
          description: Human-readable error message
          example: Application 123 not found
        details:
          description: Additional error details (optional)
          example: Additional context about the error
        requestId:
          type: string
          description: Unique request identifier for tracking and debugging
          example: 550e8400-e29b-41d4-a716-446655440000
  responses:
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        API key for authentication. Include your Leap API key in the `x-api-key`
        header: `x-api-key: leap_live_...`
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Clerk JWT token for portal authentication

````