Skip to main content

Authentication and Setup

Prerequisites

To call the K12 Safety API, you need:

CredentialWhat It IsWhere to Get It
Client IDAzure AD application identifier registered for K12 API accessYour Azure AD administrator provisions this when onboarding your application
Client SecretSecret associated with the Client IDGenerated in the Azure AD app registration portal
Tenant IDAzure AD directory (tenant) that contains your app registrationFound in the Azure AD portal under "Overview"
Subscription KeyAPI Management gateway key for the K12 endpointIssued when your application is registered in the API Management portal
note

You will never send your Client Secret directly to the K12 API. It is only used to obtain an access token from Azure AD. The access token is what you include in API requests.


Authentication

The K12 API uses OAuth 2.0 client credentials for authorization and an API Management subscription key for gateway access. Both must be present on every request.

Step 1: Obtain an Access Token

Request a token from Azure Entra (Azure AD) using the client credentials grant:

curl -X POST "https://login.microsoftonline.com/ef3e1dbf-4cc9-4a06-bc23-71dd7a46fd1b/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id={CLIENT_ID}" \
-d "client_secret={CLIENT_SECRET}" \
-d "scope={OAUTH_SCOPE}" \
-d "grant_type=client_credentials"

The response includes an access_token field. Use that value in the Authorization header for all subsequent requests.

Token response:

{
"token_type": "Bearer",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOi..."
}

Tokens expire after approximately 1 hour. Your application should cache the token and refresh it before expiry.

Step 2: Include Required Headers

Every API call must include these three headers:

HeaderValuePurpose
AuthorizationBearer {access_token}OAuth 2.0 bearer token from Step 1
Ocp-Apim-Subscription-Key{subscription_key}API Management gateway subscription key
Content-Typeapplication/jsonRequest body format

Environments

Each environment has its own gateway URL and OAuth scope. Use the scope that matches your target environment when requesting an access token.

AttributeValue
Base URLhttps://dev-k12-aitools-t27p-apim.azure-api.net
OAuth Scopeapi://dev-k12-aitools-t27p-api/.default

All examples in this documentation use the Development base URL. Replace it with the appropriate URL for your target environment.


Endpoint Patterns

Legacy Tool Execute (Current)

All K12 tools are available through the tool execution endpoint:

POST {BASE_URL}/ai/api/tools/{tool-id}/execute

Request bodies wrap tool-specific inputs inside a data envelope:

{
"data": {
"field1": "value1",
"field2": "value2"
}
}

The response structure varies by tool but always returns JSON. Kickoff tools return tracking identifiers; status tools return progress information; result tools return the completed output.

Domain Routes (Alternative)

The K12 API also exposes domain-scoped routes with cleaner URLs and a flat request body (no data wrapper):

Tools:

POST {BASE_URL}/ai/k12/tools/{slug}

Workflows:

POST {BASE_URL}/ai/k12/workflows/{slug}                -- Synchronous execution
POST {BASE_URL}/ai/k12/workflows/{slug}/start-async -- Async (returns runId)
GET {BASE_URL}/ai/k12/workflows/{slug}/runs/{runId} -- Poll run status

Key differences from the legacy endpoint:

Legacy (/ai/api/tools/.../execute)Domain (/ai/k12/tools/...)
Request body{ "data": { ... } }{ ... } (flat JSON)
Validation errors400422
URL styleGeneric tool IDDescriptive domain path
note

Both endpoint patterns reach the same underlying tools and return the same results. The examples in this documentation use the legacy endpoint pattern, but domain routes can be used interchangeably by removing the data wrapper from request bodies.


Quick Start

This example demonstrates the full flow: obtain a token, then kick off an EOP editing job.

# 1. Get a token (replace placeholders with your credentials)
TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/ef3e1dbf-4cc9-4a06-bc23-71dd7a46fd1b/oauth2/v2.0/token" \
-d "client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&scope=api://dev-k12-aitools-t27p-api/.default&grant_type=client_credentials" \
| jq -r '.access_token')

# 2. Kick off an EOP editing job
curl -X POST "https://dev-k12-aitools-t27p-apim.azure-api.net/ai/api/tools/k12-annex-editing-kickoff/execute" \
-H "Authorization: Bearer $TOKEN" \
-H "Ocp-Apim-Subscription-Key: $SUBSCRIPTION_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"emergencyOperationPlanId": "plan-abc123",
"userId": "user-456",
"roleId": "safety-coordinator",
"form": {
"name": "Fire Safety Annex",
"field": {
"group": "Response Procedures",
"name": "evacuation_steps",
"content": "<p>Current evacuation procedures.</p>"
}
},
"prompt": "Add assembly point verification step"
}
}'

# 3. Expected response
# {
# "requestId": "req-7f3a...",
# "sessionId": "sess-a1b2...",
# "status": "accepted"
# }

# 4. Poll status until terminal (success/error/cancelled)
curl -X POST "https://dev-k12-aitools-t27p-apim.azure-api.net/ai/api/tools/k12-annex-editing-status/execute" \
-H "Authorization: Bearer $TOKEN" \
-H "Ocp-Apim-Subscription-Key: $SUBSCRIPTION_KEY" \
-H "Content-Type: application/json" \
-d '{"data": {"requestIds": ["req-7f3a..."]}}'

# 5. Fetch result once status is "success"
curl -X POST "https://dev-k12-aitools-t27p-apim.azure-api.net/ai/api/tools/k12-annex-editing-result/execute" \
-H "Authorization: Bearer $TOKEN" \
-H "Ocp-Apim-Subscription-Key: $SUBSCRIPTION_KEY" \
-H "Content-Type: application/json" \
-d '{"data": {"requestId": "req-7f3a..."}}'

Standard Error Responses

StatusDescription
400Bad Request -- missing required fields or invalid format
401Unauthorized -- invalid or expired bearer token, or missing subscription key
403Forbidden -- token lacks the required scope for this environment
404Not Found -- tool ID or resource does not exist
429Too Many Requests -- rate limit exceeded
500Internal Server Error -- unexpected failure

Next Steps