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
- Authenticate to the API.
- Parse the script and resolve every flag -- pick a real credential per step, fill in any missing schedule day, etc.
- Pick a target server for each job (from List Target Servers).
- 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 missingDay). This aborts the whole request, but any jobs already created earlier in the same request are not rolled back -- checkcreatedJobIdson 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.