Перейти к содержанию

POST /v1/builds

Загружает файл сборки мобильного приложения и автоматически создаёт новый релиз. Версия и номер сборки извлекаются из метаданных файла.

Если загружается сборка с уже существующей версией, создаётся новый релиз — в дальнейшем все релизы с одинаковой версией группируются вместе.

Обработка файла выполняется асинхронно. В зависимости от размера сборки это может занять от нескольких секунд до нескольких минут.

Поддерживаемые форматы

  • Android: .apk
  • iOS: .ipa
  • Максимальный размер файла: 1 ГБ

Валидация при загрузке

При загрузке сборки сервер выполняет проверки:

  1. Соответствие расширения файла платформе приложения. Расширение загружаемого файла должно соответствовать платформе, указанной в настройках приложения: .apk — только для Android, .ipa — только для iOS. Загрузка .ipa в Android-приложение (и наоборот) вернёт ошибку 415.

  2. Соответствие идентификатора пакета. Bundle ID (iOS) или имя пакета (Android), извлечённые из метаданных файла, должны точно совпадать с идентификатором, указанным в настройках экземпляра приложения. При несовпадении сервер вернёт ошибку 400.


Request

Body multipart/form-data
file binary required
Файл сборки приложения: .apk для Android или .ipa для iOS.
description string
Описание релиза (Release Notes)
Response 202 Accepted · application/json
status string
Статус обработки: processing
message string
Сообщение о результате
buildId string
Идентификатор созданной сборки (UUID)
releaseId string
Идентификатор созданного релиза (UUID)
buildNumber integer
Порядковый номер сборки
buildVersion string
Версия, извлечённая из манифеста

Примеры запроса

curl -X POST "https://<DOMAIN>/v1/builds" \
  -H "Authorization: Bearer <API_KEY>" \
  -F "file=@app-release.apk" \
  -F "description=Release notes текст"
import requests

url = "https://<DOMAIN>/v1/builds"
headers = {"Authorization": "Bearer <API_KEY>"}
files = {"file": open("app-release.apk", "rb")}
data = {"description": "Release notes текст"}

response = requests.post(url, headers=headers, files=files, data=data)
print(response.json())
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('description', 'Release notes текст');

fetch('https://<DOMAIN>/v1/builds', {
    method: 'POST',
    headers: {
        'Authorization': 'Bearer <API_KEY>'
    },
    body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
using System.Net.Http.Headers;

var client = new HttpClient();
using var content = new MultipartFormDataContent();
using var fileStream = File.OpenRead("app-release.apk");
content.Add(new StreamContent(fileStream), "file", "app-release.apk");
content.Add(new StringContent("Release notes текст"), "description");

var request = new HttpRequestMessage(HttpMethod.Post, "https://<DOMAIN>/v1/builds");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "<API_KEY>");
request.Content = content;

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
package main

import (
    "bytes"
    "fmt"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

func main() {
    file, _ := os.Open("app-release.apk")
    defer file.Close()

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, _ := writer.CreateFormFile("file", "app-release.apk")
    io.Copy(part, file)
    writer.WriteField("description", "Release notes текст")
    writer.Close()

    req, _ := http.NewRequest("POST", "https://<DOMAIN>/v1/builds", body)
    req.Header.Set("Authorization", "Bearer <API_KEY>")
    req.Header.Set("Content-Type", writer.FormDataContentType())

    client := &http.Client{}
    res, err := client.Do(req)
    if err != nil { /* ... обработка ошибки ... */ }
    defer res.Body.Close()

    responseBody, _ := io.ReadAll(res.Body)
    fmt.Println(string(responseBody))
}
import okhttp3.*;
import java.io.File;
import java.io.IOException;

OkHttpClient client = new OkHttpClient();

RequestBody requestBody = new MultipartBody.Builder()
    .setType(MultipartBody.FORM)
    .addFormDataPart("file", "app-release.apk",
        RequestBody.create(new File("app-release.apk"), MediaType.parse("application/octet-stream")))
    .addFormDataPart("description", "Release notes текст")
    .build();

Request request = new Request.Builder()
    .url("https://<DOMAIN>/v1/builds")
    .post(requestBody)
    .addHeader("Authorization", "Bearer <API_KEY>")
    .build();

try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

Примеры ответа

{
  "status": "processing",
  "message": "The build has been saved",
  "buildId": "e891fc1d-d7ab-4be5-b5a1-228a6e6716de",
  "releaseId": "6b35f2c5-fe2e-4654-b747-b4572783af9a",
  "buildNumber": 1,
  "buildVersion": "1.0"
}
{
  "status": "error",
  "code": 4152,
  "message": "Unsupported file type for iOS."
}

Ошибки

HTTP code Описание
400 4001 Bundle ID из сборки не соответствует Bundle ID приложения
400 4002 Некорректный тип данных в description
401 Неавторизованный запрос
403 Недостаточно прав
415 4151 Загружен файл неподдерживаемого формата
415 4152 Несоответствие расширения файла платформе приложения
500 Внутренняя ошибка сервера

См. также