Actually creates jobs, steps, and schedules from a previously-parsed import. Parse never persists anything -- this is the one call that does. Every step needs a real CredentialId by this point; Parse's matchedCredentialId is only a suggestion, never good enough to commit as-is.

Having trouble reaching the API, or logging in from another machine? See Network Access & Authentication Security -- by default the Api only answers localhost, and PowerShell needs an extra flag once it does answer elsewhere.

Order of Operations

  1. Authenticate to the API.
  2. Parse the script and resolve every flag -- pick a real credential per step, fill in any missing schedule day, etc.
  3. Pick a target server for each job (from List Target Servers).
  4. Commit.

The call

POST /api/job-imports/commit

Permission needed: Job.Edit.

C# example

var jsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);

// Step 1: Authenticate to the API.
using var handler = new HttpClientHandler { UseDefaultCredentials = true };
using var client = new HttpClient(handler) { BaseAddress = new Uri("http://your-minion-agent-server:5443") };
client.DefaultRequestHeaders.Add("X-App-Name", "MyIntegration");

// Steps 2 & 3: You already parsed the script and resolved every flag -- here's one resolved job.
var jobToCommit = new ImportCommitJobRequest(
    JobName: "Nightly ETL",
    Description: "Imported nightly load",
    TargetServerId: 7,
    Steps: new[] { new ImportCommitStepRequest(StepName: "Load Staging", StepTypeCode: 0, CommandText: "EXEC dbo.LoadStaging", CredentialId: 4) },
    Schedules: new[] { new ImportCommitScheduleRequest(Day: "Daily", Ordinal: null, StartTime: TimeSpan.Parse("02:00:00"), EndTime: null, MaxForTimeframe: null, FrequencyMinutes: null, RunOnStartup: false) });

// Step 4: Commit. HistoryJson is optional -- pass a scan's ResultHistoryJson to backfill run history.
var response = await client.PostAsJsonAsync("/api/job-imports/commit", new ImportCommitRequest(new[] { jobToCommit }, HistoryJson: null), jsonOptions);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ImportCommitResponse>(jsonOptions);
Console.WriteLine($"Created job ids: {string.Join(", ", result!.CreatedJobIds)}");
foreach (var skipped in result.SkippedDuplicates)
    Console.WriteLine($"Skipped '{skipped.JobName}' -- already exists as job {skipped.ExistingJobId}.");

record ImportCommitStepRequest(string StepName, byte StepTypeCode, string CommandText, int CredentialId);
record ImportCommitScheduleRequest(string? Day, int? Ordinal, TimeSpan? StartTime, TimeSpan? EndTime, int? MaxForTimeframe, int? FrequencyMinutes, bool RunOnStartup);
record ImportCommitJobRequest(string JobName, string? Description, int TargetServerId, ImportCommitStepRequest[] Steps, ImportCommitScheduleRequest[] Schedules);
record ImportCommitRequest(ImportCommitJobRequest[] Jobs, string? HistoryJson = null);
record ImportDuplicateInfo(string JobName, int ExistingJobId);
record ImportCommitResponse(int[] CreatedJobIds, ImportDuplicateInfo[] SkippedDuplicates);

Not on a domain machine? Swap in the app-account login from Calling the API From Your Own Code.

PowerShell example

# Step 1: Authenticate to the API.
$headers = @{ "X-App-Name" = "MyIntegration" }

# Steps 2 & 3: You already parsed the script and resolved every flag.
$jobToCommit = @{
    JobName        = "Nightly ETL"
    Description    = "Imported nightly load"
    TargetServerId = 7
    Steps          = @(@{ StepName = "Load Staging"; StepTypeCode = 0; CommandText = "EXEC dbo.LoadStaging"; CredentialId = 4 })
    Schedules      = @(@{ Day = "Daily"; Ordinal = $null; StartTime = "02:00:00"; EndTime = $null; MaxForTimeframe = $null; FrequencyMinutes = $null; RunOnStartup = $false })
}

# Step 4: Commit. (-AllowUnencryptedAuthentication: PowerShell requires this for Windows auth over plain http to anything but localhost -- see "Network Access & Authentication Security".)
$body = @{ Jobs = @($jobToCommit); HistoryJson = $null } | ConvertTo-Json -Depth 10
$result = Invoke-RestMethod -Uri "http://your-minion-agent-server:5443/api/job-imports/commit" `
    -Method Post -Body $body -ContentType "application/json" -UseDefaultCredentials -AllowUnencryptedAuthentication -Headers $headers
"Created job ids: $($result.createdJobIds -join ', ')"
$result.skippedDuplicates | ForEach-Object { "Skipped '$($_.jobName)' -- already exists as job $($_.existingJobId)." }

What you get back

200 OK:

{ "createdJobIds": [301], "skippedDuplicates": [] }

Codes this call can return

See API Response Codes for what each one means in general. For this specific call:

  • 200 -- processed (check createdJobIds/skippedDuplicates -- a 200 doesn't mean every job in the request was created).
  • 400 -- one of the jobs still has an unresolved step or schedule (a step with no CredentialId, no steps at all, or a non-startup schedule missing Day). This aborts the whole request, but any jobs already created earlier in the same request are not rolled back -- check createdJobIds on a 400 too if you're committing several jobs at once.
  • 403 -- you don't have Job.Edit.

A job whose name already exists isn't an error -- it's silently skipped and reported in skippedDuplicates. There's no server-side "replace" or "rename the existing job" option in this call; if you need that, delete or rename the existing job yourself first (see Delete a Job, Rename a Job), then commit.

What gets audited

Each created job is recorded individually as Job / Import Job From SQL Agent, with the job name and step/schedule counts in the after-state. A skipped duplicate isn't audited (nothing changed).

What actually gets created

Steps are wired as a straight sequential chain (each continuing on to the next); the original job's success/failure branching isn't translated. Steps are created as plain command steps -- step-pool membership and step-type-specific fields (e.g. File Ops, SSIS) aren't populated even if Parse proposed a richer step type.

See also: Parse a Job Import Script, Request a Job Import Scan, List Target Servers.