POST /v1/builds¶
Загружает файл сборки мобильного приложения и автоматически создаёт новый релиз. Версия и номер сборки извлекаются из метаданных файла.
Если загружается сборка с уже существующей версией, создаётся новый релиз — в дальнейшем все релизы с одинаковой версией группируются вместе.
Обработка файла выполняется асинхронно. В зависимости от размера сборки это может занять от нескольких секунд до нескольких минут.
Поддерживаемые форматы
- Android:
.apk - iOS:
.ipa - Максимальный размер файла: 1 ГБ
Валидация при загрузке¶
При загрузке сборки сервер выполняет проверки:
-
Соответствие расширения файла платформе приложения. Расширение загружаемого файла должно соответствовать платформе, указанной в настройках приложения:
.apk— только для Android,.ipa— только для iOS. Загрузка.ipaв Android-приложение (и наоборот) вернёт ошибку415. -
Соответствие идентификатора пакета. Bundle ID (iOS) или имя пакета (Android), извлечённые из метаданных файла, должны точно совпадать с идентификатором, указанным в настройках экземпляра приложения. При несовпадении сервер вернёт ошибку
400.
Request¶
.apk для Android или .ipa для iOS.processingПримеры запроса¶
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 | — | Внутренняя ошибка сервера |