curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/github/sync \
--header 'api_key: <api-key>'import requests
url = "https://app.base44.com/api/apps/{app_id}/github/sync"
headers = {"api_key": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {api_key: '<api-key>'}};
fetch('https://app.base44.com/api/apps/{app_id}/github/sync', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/github/sync",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/github/sync"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("api_key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/github/sync")
.header("api_key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/github/sync")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"synced": true,
"already_up_to_date": false,
"commits_pulled": 2,
"latest_commit_hash": "4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4",
"commits": [
{
"author_email": "dana@example.com",
"author_name": "Dana Levi",
"message": "Fix the lead scoring rounding",
"sha": "4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4",
"short_sha": "4c7e1f9",
"timestamp": "2026-08-15T09:08:41+00:00",
"url": "https://github.com/base44/lead-tracker/commit/4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4"
}
],
"files_summary": {
"total_files": 7,
"files_with_content": 6,
"files_metadata_only": 1,
"files_deleted": 1,
"total_additions": 214,
"total_deletions": 31
},
"error": "merge_conflict",
"error_message": "Merge conflict while applying the pulled commits",
"duration_ms": 4120
}Pull changes from GitHub
Pulls new commits from the connected GitHub repository into the app.
Call this after someone pushes to the repository, or before you read the app’s code, so Base44 is working from the latest version. It applies only what is new since the last pull, and does nothing when the app is already up to date.
A failed pull is reported in the response body rather than as an error, so read synced and error. Base44 doesn’t record the commits it couldn’t apply, so the next pull picks them up again rather than skipping past them. A pull that fails late can already have added a chat message or a checkpoint, and retrying repeats those.
A merge conflict means the repository’s commits and the app’s own changes touch the same lines. Hand it to Resolve GitHub sync conflicts.
curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/github/sync \
--header 'api_key: <api-key>'import requests
url = "https://app.base44.com/api/apps/{app_id}/github/sync"
headers = {"api_key": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {api_key: '<api-key>'}};
fetch('https://app.base44.com/api/apps/{app_id}/github/sync', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/github/sync",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/github/sync"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("api_key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/github/sync")
.header("api_key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/github/sync")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"synced": true,
"already_up_to_date": false,
"commits_pulled": 2,
"latest_commit_hash": "4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4",
"commits": [
{
"author_email": "dana@example.com",
"author_name": "Dana Levi",
"message": "Fix the lead scoring rounding",
"sha": "4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4",
"short_sha": "4c7e1f9",
"timestamp": "2026-08-15T09:08:41+00:00",
"url": "https://github.com/base44/lead-tracker/commit/4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4"
}
],
"files_summary": {
"total_files": 7,
"files_with_content": 6,
"files_metadata_only": 1,
"files_deleted": 1,
"total_additions": 214,
"total_deletions": 31
},
"error": "merge_conflict",
"error_message": "Merge conflict while applying the pulled commits",
"duration_ms": 4120
}Authorizations
Personal API key.
Path Parameters
ID of the app whose GitHub sync this affects.
Response
The outcome of the pull.
Outcome of a pull from the connected repository.
Whether this call applied new commits to the app.
true
Whether the repository's head was already the last commit Base44 pulled, so there was nothing to do.
false
How many commits commits holds, so commits Base44 pushed itself aren't counted. The value is 0 when nothing was pulled.
2
Repository head after the pull, or null when nothing was pulled.
"4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4"
The commits this call applied, oldest first. Commits Base44 pushed itself are left out, so a pull of only those comes back with an empty list.
Show child attributes
Show child attributes
[ { "author_email": "dana@example.com", "author_name": "Dana Levi", "message": "Fix the lead scoring rounding", "sha": "4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4", "short_sha": "4c7e1f9", "timestamp": "2026-08-15T09:08:41+00:00", "url": "https://github.com/base44/lead-tracker/commit/4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4" } ]
Change counts for the pulled commits, or null when nothing was pulled.
Show child attributes
Show child attributes
Why the pull didn't happen, or null when it succeeded. One of not_connected, no_installation, sync_in_progress, connection_error, merge_conflict, sandbox_sync_failed, rate_limit_exceeded, rate_limit_low, github_api_error or unexpected_error.
"merge_conflict"
Human-readable explanation of error, or null when the pull succeeded.
"Merge conflict while applying the pulled commits"
How long the pull took, in milliseconds, or null when Base44 recorded no duration for it.
4120
Was this page helpful?