Skip to content

Latest commit

Β 

History

History
932 lines (703 loc) Β· 22.5 KB

File metadata and controls

932 lines (703 loc) Β· 22.5 KB

Application Upload API - Complete Documentation

Table of Contents

  1. API Overview
  2. Understanding Versions
  3. API Request Parameters
  4. API Response Formats
  5. Version Action Types
  6. Complete Usage Scenarios
  7. Email Notifications
  8. CI/CD Integration
  9. Error Handling
  10. FAQ

API Overview

Endpoint: POST /v1/application

Purpose: Upload Android APK files to NativeBridge platform for testing and distribution.

Rate Limit: 10 requests per minute

Authentication: Required via X-Api-Key header


Understanding Versions

Two Types of Versions

The system tracks TWO different version numbers:

1. APK Version (from AndroidManifest.xml)

  • Source: Extracted from your APK's AndroidManifest.xml
  • Field Names: versionName and versionCode
  • Examples: "1.2.3", "2.0.0-beta", "v3.5.1-rc1"
  • Format: Any string you set in your app
  • Purpose: Identifies your actual app version (what users see)
  • Control: Set by you in your app's build.gradle
  • Stored As: application_meta_data.package_info.version_name

2. NativeBridge Version (Internal System Tracking)

  • Source: Auto-generated by NativeBridge
  • Field Name: application_version / nb_version
  • Examples: "1.0", "2.0", "3.0"
  • Format: Always numeric X.0 (major version only)
  • Purpose: Tracks upload sequence on NativeBridge platform
  • Control: Automatically managed by the system
  • Stored As: application_version and latest_application_version

Why Two Versions?

Scenario: You upload your app 3 times with different APK versions

Upload 1: APK v1.0.0     β†’ NativeBridge v1.0
Upload 2: APK v1.5.2     β†’ NativeBridge v2.0
Upload 3: APK v2.0.0-rc1 β†’ NativeBridge v3.0

Your APK versions can be anything you want.
NativeBridge versions always increment sequentially: 1.0, 2.0, 3.0...

API Request Parameters

Request Format

Content-Type: multipart/form-data

Headers:

X-Api-Key: your_api_key_here
Content-Type: multipart/form-data

Parameters

Parameter Type Required Default Description
file UploadFile No* - APK file to upload (binary data)
apkUrl String No* - Public URL to download APK from
accessType Enum No PUBLIC Access control: PUBLIC or PRIVATE
allowedUsers Array[String] No [] List of email addresses (required if accessType=PRIVATE)
versionAction Enum No CREATE_NEW_VERSION Action when app exists: CREATE_NEW_VERSION, CREATE_NEW_APP, or DO_NOTHING
sendNotification Boolean No false Send email notification after successful upload
notificationEmails Array[String] No [] Email addresses to notify (defaults to uploader's email if empty)

Note: Either file or apkUrl must be provided (not both).

Parameter Details

file (UploadFile)

  • Binary APK file data
  • Must be a valid Android APK file
  • File extension: .apk

apkUrl (String)

  • Alternative to direct file upload
  • Must be a publicly accessible URL
  • URL must return an APK file when accessed
  • Example: https://example.com/releases/app-v1.2.3.apk

accessType (Enum)

  • PUBLIC: Anyone with the magic link can access
  • PRIVATE: Only specified users can access (requires allowedUsers)

allowedUsers (Array[String])

  • List of email addresses that can access the app
  • Required when accessType=PRIVATE
  • Example: ["user1@example.com", "user2@example.com"]
  • Empty strings are automatically filtered out

versionAction (Enum)

Determines behavior when an app with the same package name already exists:

  • CREATE_NEW_VERSION (default):

    • Creates a new version of the existing app
    • NativeBridge version auto-increments (1.0 β†’ 2.0 β†’ 3.0)
    • Same short_id and magic link
    • Version history is preserved
  • CREATE_NEW_APP:

    • Creates a completely new application entry
    • New short_id and different magic link
    • Version starts at 1.0
    • Can have multiple apps with same package name
  • DO_NOTHING:

    • Returns 409 error if app exists
    • Use for validation/checking if app exists

sendNotification (Boolean)

  • true: Send email notification after successful upload
  • false: No email notification
  • Default: false

notificationEmails (Array[String])

  • Email addresses to receive upload notification
  • If empty and sendNotification=true, sends to uploader's email
  • Example: ["dev@example.com", "qa@example.com"]
  • Invalid emails are filtered out with warnings

API Response Formats

Success Response (200 OK)

{
  "data": {
    "id": "3qeh",
    "magicLink": "https://nativebridge.io/app/3qeh",
    "versionedMagicLink": "https://nativebridge.io/app/3qeh?version=1.0",
    "accessType": "public",
    "version": "1.0"
  }
}

Response Fields

Field Type Description Example
id String Short ID for the application "3qeh"
magicLink String Direct link to access the latest version "https://nativebridge.io/app/3qeh"
versionedMagicLink String Link to access specific version "https://nativebridge.io/app/3qeh?version=1.0"
accessType String Access control setting "public" or "private"
version String NativeBridge version assigned "1.0", "2.0", "3.0", etc.

Magic Link vs Versioned Magic Link

  • magicLink: Always points to the latest version

    • Example: https://nativebridge.io/app/3qeh
    • If you upload v2.0, this link will show v2.0
  • versionedMagicLink: Points to the specific version you just uploaded

    • Example: https://nativebridge.io/app/3qeh?version=1.0
    • Will always open version 1.0, even after uploading v2.0

Error Responses

400 Bad Request - Missing File

{
  "detail": "Either file or apkUrl must be provided"
}

400 Bad Request - Invalid Email

{
  "detail": "Invalid email format in allowed users"
}

401 Unauthorized - Missing API Key

{
  "detail": "Missing API Key"
}

401 Unauthorized - Invalid API Key

{
  "detail": "Invalid API Key"
}

409 Conflict - App Already Exists

{
  "detail": "Application with package name 'com.example.app' already exists. Use versionAction='create_new_version' to create a new version or 'create_new_app' to create a separate app entry."
}

This occurs when versionAction=DO_NOTHING and an app with the same package name exists.

429 Too Many Requests - Rate Limit

{
  "detail": "Rate limit exceeded. Please try again later."
}

500 Internal Server Error

{
  "detail": "Failed to upload file to GCS: [error message]"
}

Version Action Types

1. CREATE_NEW_VERSION (Default)

Behavior:

  • Creates a new version of the existing application
  • NativeBridge version auto-increments
  • Preserves version history
  • Same magic link (short_id doesn't change)

Use Case:

  • Regular app updates
  • Bug fixes and feature releases
  • CI/CD automated deployments

Example:

# First upload
curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: your_api_key" \
  -F "file=@app-v1.0.0.apk"

# Response: version=1.0, id=abc123, magicLink=.../app/abc123

# Second upload (same package name)
curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: your_api_key" \
  -F "file=@app-v2.0.0.apk"

# Response: version=2.0, id=abc123, magicLink=.../app/abc123 (same ID!)

2. CREATE_NEW_APP

Behavior:

  • Creates a completely new application entry
  • New short_id and different magic link
  • Version starts at 1.0
  • Independent from existing app

Use Case:

  • Creating variants of the same app (e.g., staging vs production)
  • Multiple builds with same package name
  • Parallel testing of different configurations

Example:

# First upload (production)
curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: your_api_key" \
  -F "file=@app-prod.apk"

# Response: version=1.0, id=abc123, magicLink=.../app/abc123

# Second upload (staging - same package name!)
curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: your_api_key" \
  -F "file=@app-staging.apk" \
  -F "versionAction=create_new_app"

# Response: version=1.0, id=xyz789, magicLink=.../app/xyz789 (different ID!)

3. DO_NOTHING

Behavior:

  • Returns 409 error if app exists
  • Does not upload or create anything
  • Useful for validation

Use Case:

  • Check if app already exists before uploading
  • Prevent accidental overwrites
  • Validation in scripts

Example:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: your_api_key" \
  -F "file=@app.apk" \
  -F "versionAction=do_nothing"

# If app exists: 409 Conflict error
# If app doesn't exist: 200 Success with version=1.0

Complete Usage Scenarios

Scenario 1: First Time Upload

Situation: No existing app with this package name

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "file=@MyApp-v1.0.0.apk" \
  -F "accessType=public"

Response:

{
  "data": {
    "id": "a1b2c3",
    "magicLink": "https://nativebridge.io/app/a1b2c3",
    "versionedMagicLink": "https://nativebridge.io/app/a1b2c3?version=1.0",
    "accessType": "public",
    "version": "1.0"
  }
}

Result:

  • New app created
  • NativeBridge version: 1.0
  • APK version from manifest: 1.0.0
  • Magic link generated

Scenario 2: Update Existing App (Default)

Situation: App with package com.example.myapp exists (v1.0)

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "file=@MyApp-v2.0.0.apk"
# No versionAction specified = CREATE_NEW_VERSION (default)

Response:

{
  "data": {
    "id": "a1b2c3",
    "magicLink": "https://nativebridge.io/app/a1b2c3",
    "versionedMagicLink": "https://nativebridge.io/app/a1b2c3?version=2.0",
    "accessType": "public",
    "version": "2.0"
  }
}

Result:

  • New version created (v2.0)
  • Same short_id (a1b2c3)
  • Same magic link
  • Version history preserved
  • Both v1.0 and v2.0 accessible via versioned links

Scenario 3: Create Separate App Entry

Situation: Want to create staging build with same package name

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "file=@MyApp-staging.apk" \
  -F "versionAction=create_new_app"

Response:

{
  "data": {
    "id": "x9y8z7",
    "magicLink": "https://nativebridge.io/app/x9y8z7",
    "versionedMagicLink": "https://nativebridge.io/app/x9y8z7?version=1.0",
    "accessType": "public",
    "version": "1.0"
  }
}

Result:

  • Completely new app created
  • Different short_id (x9y8z7)
  • Different magic link
  • Independent from existing app
  • Both apps coexist with same package name

Scenario 4: Private App with Allowed Users

Situation: Upload private app for specific team members

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "file=@MyApp.apk" \
  -F "accessType=private" \
  -F "allowedUsers=dev@company.com" \
  -F "allowedUsers=qa@company.com"

Response:

{
  "data": {
    "id": "p1r2v3",
    "magicLink": "https://nativebridge.io/app/p1r2v3",
    "versionedMagicLink": "https://nativebridge.io/app/p1r2v3?version=1.0",
    "accessType": "private",
    "version": "1.0"
  }
}

Result:

  • Private app created
  • Only dev@company.com and qa@company.com can access
  • Magic link requires authentication

Scenario 5: Upload with Email Notifications

Situation: Upload app and notify QA team

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "file=@MyApp-v3.0.0.apk" \
  -F "sendNotification=true" \
  -F "notificationEmails=qa-team@company.com" \
  -F "notificationEmails=product@company.com"

Response:

{
  "data": {
    "id": "a1b2c3",
    "magicLink": "https://nativebridge.io/app/a1b2c3",
    "versionedMagicLink": "https://nativebridge.io/app/a1b2c3?version=3.0",
    "accessType": "public",
    "version": "3.0"
  }
}

Result:

  • New version created (v3.0)
  • Email sent to qa-team@company.com and product@company.com
  • Email contains app details, version info, and magic link
  • Upload completes immediately (email sent asynchronously)

Scenario 6: Upload from URL

Situation: APK hosted on external server

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "apkUrl=https://builds.company.com/releases/app-v1.2.3.apk"

Response:

{
  "data": {
    "id": "u1r2l3",
    "magicLink": "https://nativebridge.io/app/u1r2l3",
    "versionedMagicLink": "https://nativebridge.io/app/u1r2l3?version=1.0",
    "accessType": "public",
    "version": "1.0"
  }
}

Result:

  • APK downloaded from provided URL
  • App created with downloaded APK
  • Same features as file upload

Scenario 7: Check if App Exists

Situation: Validate before uploading

Request:

curl -X POST https://api.nativebridge.io/v1/application \
  -H "X-Api-Key: sk_live_abc123xyz" \
  -F "file=@MyApp.apk" \
  -F "versionAction=do_nothing"

Response (if app exists):

{
  "detail": "Application with package name 'com.example.myapp' already exists. Use versionAction='create_new_version' to create a new version or 'create_new_app' to create a separate app entry."
}

Status: 409 Conflict

Response (if app doesn't exist):

{
  "data": {
    "id": "n1e2w3",
    "magicLink": "https://nativebridge.io/app/n1e2w3",
    "versionedMagicLink": "https://nativebridge.io/app/n1e2w3?version=1.0",
    "accessType": "public",
    "version": "1.0"
  }
}

Status: 200 OK


Email Notifications

Email Content

When sendNotification=true, recipients receive a beautifully formatted email containing:

Email Subject:

πŸš€ [App Name] - Successfully Uploaded to NativeBridge

Email Body Includes:

  • App icon (if extracted from APK)
  • Application name
  • Package name
  • APK version (from manifest)
  • NativeBridge version
  • File type (APK/AAB)
  • Access type (Public/Private)
  • Versioned magic link (e.g., https://nativebridge.io/app/abc123?version=2.0)
  • App ID (short_id)
  • Upload timestamp
  • Uploader email
  • "Open Application" button
  • Next steps guidance

Email Features

  • Responsive Design: Works on mobile and desktop
  • Dark Mode Support: Automatically adapts to user's theme preference
  • Versioned Links: Email contains versioned magic link to specific upload
  • Icon Embedding: App icon embedded using signed URLs (7-day expiration)
  • Beautiful Layout: Professional gradient header, card-based design

Notification Recipients

If notificationEmails is provided and not empty:

  • Sends to all specified email addresses
  • Invalid emails are filtered out with warnings

If notificationEmails is empty or not provided:

  • Sends to uploader's email (from API key)

Example:

# Send to specific recipients
-F "sendNotification=true" \
-F "notificationEmails=team@company.com"

# Send to uploader (email associated with API key)
-F "sendNotification=true"

CI/CD Integration

GitHub Actions Example

name: Deploy to NativeBridge
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Build APK
        run: ./gradlew assembleRelease

      - name: Upload to NativeBridge
        run: |
          curl -X POST https://api.nativebridge.io/v1/application \
            -H "X-Api-Key: ${{ secrets.NATIVEBRIDGE_API_KEY }}" \
            -F "file=@app/build/outputs/apk/release/app-release.apk" \
            -F "sendNotification=true" \
            -F "notificationEmails=${{ secrets.QA_EMAIL }}"

GitLab CI Example

deploy:
  stage: deploy
  script:
    - ./gradlew assembleRelease
    - |
      curl -X POST https://api.nativebridge.io/v1/application \
        -H "X-Api-Key: ${NATIVEBRIDGE_API_KEY}" \
        -F "file=@app/build/outputs/apk/release/app-release.apk" \
        -F "versionAction=create_new_version"
  only:
    - main

Jenkins Pipeline Example

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh './gradlew assembleRelease'
            }
        }
        stage('Deploy') {
            steps {
                sh '''
                    curl -X POST https://api.nativebridge.io/v1/application \
                        -H "X-Api-Key: ${NATIVEBRIDGE_API_KEY}" \
                        -F "file=@app/build/outputs/apk/release/app-release.apk" \
                        -F "sendNotification=true" \
                        -F "notificationEmails=qa@company.com"
                '''
            }
        }
    }
}

Bitrise Example

workflows:
  deploy:
    steps:
    - gradle-runner@2:
        inputs:
        - gradle_task: assembleRelease
    - script@1:
        inputs:
        - content: |
            #!/bin/bash
            curl -X POST https://api.nativebridge.io/v1/application \
              -H "X-Api-Key: $NATIVEBRIDGE_API_KEY" \
              -F "file=@$BITRISE_APK_PATH"

Error Handling

Common Errors and Solutions

Error: "Missing API Key"

Status: 401 Unauthorized

Cause: No X-Api-Key header provided

Solution:

# Add header
-H "X-Api-Key: your_api_key_here"

Error: "Invalid API Key"

Status: 401 Unauthorized

Cause: API key doesn't exist or is invalid

Solution:

  • Verify API key is correct
  • Generate new API key from dashboard
  • Check for typos or extra spaces

Error: "Either file or apkUrl must be provided"

Status: 400 Bad Request

Cause: Neither file nor apkUrl parameter provided

Solution:

# Provide file
-F "file=@app.apk"

# OR provide URL
-F "apkUrl=https://example.com/app.apk"

Error: "Invalid email format in allowed users"

Status: 400 Bad Request

Cause: Invalid email address in allowedUsers array

Solution:

# Ensure valid email format
-F "allowedUsers=user@example.com"  # βœ“ Valid
-F "allowedUsers=invalid-email"     # βœ— Invalid

Error: "Application with package name 'X' already exists"

Status: 409 Conflict

Cause: App exists and versionAction=do_nothing

Solution:

# Create new version (default)
-F "versionAction=create_new_version"

# OR create separate app
-F "versionAction=create_new_app"

Error: "Rate limit exceeded"

Status: 429 Too Many Requests

Cause: More than 10 requests per minute

Solution:

  • Wait 60 seconds before retrying
  • Implement exponential backoff in CI/CD
  • Contact support for higher rate limits

Error: "Failed to upload file to GCS"

Status: 500 Internal Server Error

Cause: Storage service issue

Solution:

  • Retry the request
  • Verify APK file is not corrupted
  • Contact support if issue persists

FAQ

Q: What happens to old versions when I upload a new one?

A: Old versions are preserved when using versionAction=create_new_version (default). All versions remain accessible via their versioned magic links.

Example:

  • v1.0: https://nativebridge.io/app/abc123?version=1.0
  • v2.0: https://nativebridge.io/app/abc123?version=2.0
  • Latest: https://nativebridge.io/app/abc123

Q: Can I have multiple apps with the same package name?

A: Yes! Use versionAction=create_new_app to create a separate app entry with a different short_id and magic link.


Q: What's the difference between magicLink and versionedMagicLink?

A:

  • magicLink: Always shows the latest version
  • versionedMagicLink: Always shows the specific version you uploaded

Example:

{
  "magicLink": "https://nativebridge.io/app/abc123",           // Shows v3.0 after 3 uploads
  "versionedMagicLink": "https://nativebridge.io/app/abc123?version=2.0"  // Always shows v2.0
}

Q: How do I know which version is currently deployed?

A: Check the version field in the API response. This is the NativeBridge version number.


Q: Can I upload from a private URL?

A: The apkUrl must be publicly accessible (no authentication required). For private URLs, download the APK first and use the file parameter.


Q: What file types are supported?

A: Currently only .apk files are supported. AAB support may be added in the future.


Q: How long are magic links valid?

A: Magic links are permanent and never expire. You can share them anytime.


Q: Do I get charged for storing multiple versions?

A: Contact your account manager for pricing details on version storage.


Q: Can I delete old versions?

A: Version deletion is not currently supported via API. Contact support for manual deletion.


Q: What happens if my APK is invalid?

A: The API will return an error during APK processing. Ensure your APK:

  • Is a valid Android APK file
  • Has a valid AndroidManifest.xml
  • Is not corrupted
  • Has proper package name and version info

Q: Can I upload without email notifications?

A: Yes! Simply omit sendNotification parameter or set it to false (default behavior).


Q: How quickly are email notifications sent?

A: Emails are sent asynchronously within seconds of upload completion. The API response is returned immediately without waiting for email delivery.


Q: What if an email address in notificationEmails is invalid?

A: Invalid emails are filtered out with a warning logged. Valid emails still receive notifications. If no valid emails remain, the notification is skipped.


Q: Can I customize the email notification template?

A: Not via API. Contact support for custom branding options.


Q: What information is included in email notifications?

A: See the Email Notifications section for complete details.


Need Help?


Last Updated: 2025-11-29 API Version: v1 Document Version: 3.0