- API Overview
- Understanding Versions
- API Request Parameters
- API Response Formats
- Version Action Types
- Complete Usage Scenarios
- Email Notifications
- CI/CD Integration
- Error Handling
- FAQ
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
The system tracks TWO different version numbers:
- Source: Extracted from your APK's
AndroidManifest.xml - Field Names:
versionNameandversionCode - 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
- 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_versionandlatest_application_version
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...
Content-Type: multipart/form-data
Headers:
X-Api-Key: your_api_key_here
Content-Type: multipart/form-data
| 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).
- Binary APK file data
- Must be a valid Android APK file
- File extension:
.apk
- 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
PUBLIC: Anyone with the magic link can accessPRIVATE: Only specified users can access (requiresallowedUsers)
- 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
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_idand magic link - Version history is preserved
-
CREATE_NEW_APP:- Creates a completely new application entry
- New
short_idand 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
true: Send email notification after successful uploadfalse: No email notification- Default:
false
- 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
{
"data": {
"id": "3qeh",
"magicLink": "https://nativebridge.io/app/3qeh",
"versionedMagicLink": "https://nativebridge.io/app/3qeh?version=1.0",
"accessType": "public",
"version": "1.0"
}
}| 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. |
-
magicLink: Always points to the latest version- Example:
https://nativebridge.io/app/3qeh - If you upload v2.0, this link will show v2.0
- Example:
-
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
- Example:
{
"detail": "Either file or apkUrl must be provided"
}{
"detail": "Invalid email format in allowed users"
}{
"detail": "Missing API Key"
}{
"detail": "Invalid API Key"
}{
"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.
{
"detail": "Rate limit exceeded. Please try again later."
}{
"detail": "Failed to upload file to GCS: [error message]"
}Behavior:
- Creates a new version of the existing application
- NativeBridge version auto-increments
- Preserves version history
- Same magic link (
short_iddoesn'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!)Behavior:
- Creates a completely new application entry
- New
short_idand 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!)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.0Situation: 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
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
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
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.comandqa@company.comcan access - Magic link requires authentication
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.comandproduct@company.com - Email contains app details, version info, and magic link
- Upload completes immediately (email sent asynchronously)
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
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
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
- 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
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"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 }}"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:
- mainpipeline {
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"
'''
}
}
}
}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"Status: 401 Unauthorized
Cause: No X-Api-Key header provided
Solution:
# Add header
-H "X-Api-Key: your_api_key_here"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
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"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" # β InvalidStatus: 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"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
Status: 500 Internal Server Error
Cause: Storage service issue
Solution:
- Retry the request
- Verify APK file is not corrupted
- Contact support if issue persists
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
A: Yes! Use versionAction=create_new_app to create a separate app entry with a different short_id and magic link.
A:
magicLink: Always shows the latest versionversionedMagicLink: 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
}A: Check the version field in the API response. This is the NativeBridge version number.
A: The apkUrl must be publicly accessible (no authentication required). For private URLs, download the APK first and use the file parameter.
A: Currently only .apk files are supported. AAB support may be added in the future.
A: Magic links are permanent and never expire. You can share them anytime.
A: Contact your account manager for pricing details on version storage.
A: Version deletion is not currently supported via API. Contact support for manual deletion.
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
A: Yes! Simply omit sendNotification parameter or set it to false (default behavior).
A: Emails are sent asynchronously within seconds of upload completion. The API response is returned immediately without waiting for email delivery.
A: Invalid emails are filtered out with a warning logged. Valid emails still receive notifications. If no valid emails remain, the notification is skipped.
A: Not via API. Contact support for custom branding options.
A: See the Email Notifications section for complete details.
- Documentation: https://docs.nativebridge.io
- API Support: api-support@nativebridge.io
- Dashboard: https://dashboard.nativebridge.io
Last Updated: 2025-11-29 API Version: v1 Document Version: 3.0