> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omni.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk update model YAML files with the API

> Use a model branch to batch programmatic YAML updates into a single git sync.

export const categoryIcons = {
  'administration': 'lock',
  'api': 'terminal',
  'connections': 'database',
  'dashboards': 'table-columns',
  'embed': 'code',
  'errors': 'exclamation',
  'modeling': 'wrench',
  'patterns': 'plus',
  'schedules & alerts': 'envelope',
  'visualizations': 'chart-column',
  'workbooks': 'book'
};

export const GuideSidebar = ({category, relatedLinks, updatedDate}) => {
  const [progress, setProgress] = React.useState(0);
  React.useEffect(() => {
    const sidebar = document.querySelector('.guide-sidebar');
    if (!sidebar) return;
    let container = sidebar.parentElement;
    while (container && !container.querySelector('.guide-header')) {
      container = container.parentElement;
    }
    if (container && !container.classList.contains('guide-page-layout')) {
      container.classList.add('guide-page-layout');
    }
  }, []);
  React.useEffect(() => {
    const handleScroll = () => {
      const scrollTop = window.scrollY;
      const docHeight = document.documentElement.scrollHeight - window.innerHeight;
      const scrollPercent = docHeight > 0 ? scrollTop / docHeight * 100 : 0;
      setProgress(Math.min(100, Math.max(0, scrollPercent)));
    };
    window.addEventListener('scroll', handleScroll, {
      passive: true
    });
    handleScroll();
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
  const icon = category ? categoryIcons[category.toLowerCase()] || 'book' : 'book';
  return <aside className="guide-sidebar">
      <div className="guide-sidebar-content">
        <a href="/guides" className="guide-sidebar-back">
          <Icon icon="arrow-left" iconType="solid" size={14} />
          <span>All guides</span>
        </a>

        <div className="guide-sidebar-section">
          <div className="guide-sidebar-label">Progress</div>
          <div className="guide-sidebar-progress">
            <div className="guide-mascot">
              <svg viewBox="0 0 450 450" width="48" height="48">
                <defs>
                  <clipPath id="progressClip">
                    <rect x="0" y={450 - progress * 4.5} width="450" height={progress * 4.5} />
                  </clipPath>
                  <linearGradient id="blobbyGradient" x1="55.9753" y1="0" x2="492.197" y2="169.724" gradientUnits="userSpaceOnUse">
                    <stop stopColor="#BCA2F3" />
                    <stop offset="0.572917" stopColor="#FF7AA2" />
                    <stop offset="1" stopColor="#F3D4A2" />
                  </linearGradient>
                </defs>

                {}
                <circle cx="223.901" cy="223.901" r="213.901" transform="matrix(-0.999988 -0.0049013 0.00491945 -0.999988 447.797 449.992)" fill="#FAFAFA" stroke="#480B38" strokeWidth="20" />

                {}
                <circle cx="223.901" cy="223.901" r="213.901" transform="matrix(-0.999988 -0.0049013 0.00491945 -0.999988 447.797 449.992)" fill="url(#blobbyGradient)" stroke="#480B38" strokeWidth="20" clipPath="url(#progressClip)" />

                {}
                <path d="M310.41 195.084C310.41 200.052 301.362 212.472 284.328 212.472C266.585 212.472 258.246 201.294 258.246 195.912" stroke="#480B38" strokeWidth="17.3883" strokeMiterlimit="1.33344" strokeLinecap="round" />
                <circle cx="21.168" cy="21.168" r="21.168" transform="matrix(-1 0 0 1 388.658 169.001)" fill="#480B38" />
                <circle cx="21.168" cy="21.168" r="21.168" transform="matrix(-1 0 0 1 223.467 169.001)" fill="#480B38" />
              </svg>
            </div>
            <span className="guide-sidebar-progress-text">{Math.round(progress)}%</span>
          </div>
        </div>

        {category && <div className="guide-sidebar-section">
            <div className="guide-sidebar-label">Category</div>
            <div className="guide-sidebar-category">
              <Icon icon={icon} iconType="solid" size={14} />
              <span>{category}</span>
            </div>
          </div>}

        {updatedDate && <div className="guide-sidebar-section">
            <div className="guide-sidebar-label">Last updated</div>
            <div className="guide-sidebar-date">{updatedDate}</div>
          </div>}

        {relatedLinks && relatedLinks.length > 0 && <div className="guide-sidebar-section">
            <div className="guide-sidebar-label">Related</div>
            <ul className="guide-sidebar-links">
              {relatedLinks.map((link, index) => <li key={index}>
                  <a href={link.href}>{link.title}</a>
                </li>)}
            </ul>
          </div>}
      </div>
    </aside>;
};

export const GuideTitle = ({title}) => {
  return <div className="guide-header">
      <h1 className="guide-title">{title}</h1>
    </div>;
};

<GuideSidebar
  categoryIcons={categoryIcons}
  category="api"
  updatedDate="May 2026"
  relatedLinks={[
{ title: "API authentication", href: "/api/authentication" },
{ title: "Branch Mode", href: "/content/develop/branch-mode" },
{ title: "Create or update YAML files", href: "/api/models/create-or-update-yaml-files" },
{ title: "Merge a branch", href: "/api/model-branches/merge-a-branch" },
{ title: "Omni API rate limiting", href: "/api/rate-limits" }
]}
/>

<GuideTitle title="Bulk update model YAML files with the API" />

The [Create or update YAML files endpoint](/api/models/create-or-update-yaml-files) writes one file per request. When you target a [git-enabled](/integrations/git) shared model directly, each write triggers its own git sync. At hundreds or thousands of files, that results in a process that could take hours.

The fix is to write against a [model branch](/content/develop/branch-mode) instead, then merge the branch once when all updates are complete. Git syncs a single time, on the merge.

## Requirements

To follow the steps in this guide, you'll need:

* An [Omni API key](/api/authentication)
* **Modeler** or higher permissions on the shared model you want to update
* The shared model's `modelId` and `connectionId`, which you can retrieve using the [List models](/api/models/list-models) endpoint

<Steps titleSize="h2">
  <Step title="Create a branch off the shared model">
    Call the [Create model](/api/models/create-model) endpoint with `modelKind: "BRANCH"`. Set `baseModelId` to the shared model you want to branch from and reuse its `connectionId`.

    ```bash theme={null}
    curl -X POST https://<your-subdomain>.omniapp.co/api/v1/models \
      -H "Authorization: Bearer <YOUR_API_TOKEN>" \
      -H "Content-Type: application/json" \
      -d '{
        "modelKind": "BRANCH",
        "baseModelId": "<SHARED_MODEL_ID>",
        "connectionId": "<CONNECTION_ID>",
        "modelName": "<MODEL_NAME>"
      }'
    ```

    Save two values from the response:

    * `id` — The branch's UUID, which you'll pass as `branchId` in the next step.
    * `modelName` — What you provided in the request body. The merge endpoint identifies the branch by name, not by ID, so you need this exact string later.
  </Step>

  <Step title="Write YAML files against the branch">
    For each file, call the [Create or update YAML files](/api/models/create-or-update-yaml-files) endpoint:

    ```bash theme={null}
    curl -X POST https://<your-subdomain>.omniapp.co/api/v1/models/<SHARED_MODEL_ID>/yaml \
      -H "Authorization: Bearer <YOUR_API_TOKEN>" \
      -H "Content-Type: application/json" \
      -d '{
        "branchId": "<BRANCH_ID>",
        "fileName": "<view_name>.view",
        "yaml": "<file contents as a string>",
        "mode": "combined"
      }'
    ```

    In the path, put the shared model's ID. For the request body:

    * `branchId` - The branch UUID from step 1's response (`id`). This routes the write operation to the branch instead of the shared model and is required for models where pull requests are required.
    * `fileName` - The name of the file being created or updated. This can be `model`, `relationships`, `<topic_name>.topic` for topics, or `<view_name>.view` for view files.
    * `yaml` - The YAML content of the file as a formatted JSON string
    * `mode` - Set to `combined`
    * `commitMessage` - Required if the model has git enabled. A short commit message for the change.

    Loop this call for every file in your batch. Because writes go to the branch, none of them triggers a git sync.

    If a single write fails partway through, the branch keeps the writes that already succeeded. Fix the failing file and retry just that request before moving on to the merge.
  </Step>

  <Step title="Validate before merging">
    Validation is optional but strongly recommended — it catches issues on the branch before they reach the shared model, where they could break existing dashboards and queries.

    Two endpoints validate different surfaces: **Validate model** for the YAML itself, and **Validate content** for the dashboards and queries that depend on it. Run both for the safest merge.

    * **Validate the model.** Use the [Validate model](/api/models/validate-model) endpoint to catch broken references, invalid types, and structural issues introduced by your YAML writes.

      ```bash theme={null}
      curl -X GET "https://<your-subdomain>.omniapp.co/api/v1/models/<SHARED_MODEL_ID>/validate?branchId=<BRANCH_ID>" \
        -H "Authorization: Bearer <YOUR_API_TOKEN>"
      ```

    * **Validate content.** Use the [Validate content](/api/content-validator/validate-content) endpoint to catch dashboards and queries that reference fields or views the branch renamed or removed. Note the query parameter is `branch_id` (snake\_case) on this endpoint.

      ```bash theme={null}
      curl -X GET "https://<your-subdomain>.omniapp.co/api/v1/models/<SHARED_MODEL_ID>/content-validator?branch_id=<BRANCH_ID>" \
        -H "Authorization: Bearer <YOUR_API_TOKEN>"
      ```

    Resolve any issues these endpoints return by writing additional YAML updates against the same branch, then re-run validation. Only proceed to the merge once both come back clean.
  </Step>

  <Step title="Merge the branch">
    <Note>
      The Merge branch endpoint can't be used with models that require pull requests or those using [git follower mode](/integrations/git/follower-mode). See the [Merge branch documentation](/api/model-branches/merge-a-branch) for more information.
    </Note>

    Once every file has been written and validated, merge the branch into the shared model. Use the `modelName` you saved from the step 1 response as the `<BRANCH_MODEL_NAME>` in the path:

    ```bash theme={null}
    curl -X POST https://<your-subdomain>.omniapp.co/api/v1/models/<SHARED_MODEL_ID>/branch/<BRANCH_MODEL_NAME>/merge \
      -H "Authorization: Bearer <YOUR_API_TOKEN>" \
      -H "Content-Type: application/json" \
      -d '{
        "delete_branch": true,
        "commit_message": "Bulk YAML import via API"
      }'
    ```

    Set `delete_branch: true` to clean up the branch after the merge succeeds.

    A successful response returns `success: true` and for git-enabled models, `git_synced: true`. This is the one git sync for the entire batch of updates.
  </Step>
</Steps>

## Next steps

* [Branch Mode](/content/develop/branch-mode) for the full concept overview and UI workflow equivalents
* [Omni API rate limiting](/api/rate-limits) to size your batch loop appropriately
* [Local development guide](/guides/modeling/local-development) for a CLI-driven alternative when you want to edit YAML in your IDE rather than scripting against the API
