Content Creation API (application token)
This API lets an external system create content in a Syntphony community without a signed-in user. It is the right choice for scheduled jobs, external CMS tools, migration or synchronization tools, and any backend-to-backend integration.
| Property | Value |
|---|---|
| Base URL | https://<your-tenant>.togo-provisioning-api.azurewebsites.net |
| Endpoint | POST app/provisioning/content/add |
| Authentication | OAuth 2.0 Client Credentials (application token) |
| Content-Type | application/json |
| Maximum request size | About 30 MB |
To only read public content, use the read-only public API instead. This page covers creating content with an application identity.
Prerequisites
Ask your Syntphony administrator for:
- An Entra ID app registration for your integration (
client_idandclient_secret). - The Tenant ID of the Syntphony Microsoft 365 tenant.
- Confirmation that your application has been granted the
access_as_applicationapp role on the Syntphony Backend API app registration, with admin consent. Without it, every call returns403 Forbidden. - The community identifiers you can publish into:
siteRelativeUrl(for example/sites/Contoso) and, optionally, the communitykey(GUID). - The
addonId(GUID) of each content type you want to create (Announcements, Applications, News/Pages). These values are specific to each tenant. - The GUIDs of any category, tag, department or location used by your content. They must already exist in the target site; the API does not create them.
Step 1: Get an application token
Request a token directly from Microsoft Entra ID with the client credentials grant.
POST https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id={client_id}
&client_secret={client_secret}
&scope={scope}{scope}has the formapi://<Backend-API-app-id>/.default. Ask your administrator for the exact value.
{
"token_type": "Bearer",
"expires_in": 3599,
"ext_expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1Qi..."
}Use access_token as a Bearer token on every call. Tokens last about one hour: cache the token and refresh it before it expires instead of requesting a new one per call.
$tenantId = "<TENANT_ID>"
$clientId = "<CLIENT_ID>"
$clientSecret = "<CLIENT_SECRET>"
$scope = "api://<Backend-API-app-id>/.default"
$tokenBody = @{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = $scope
}
$tokenResponse = Invoke-RestMethod -Method Post `
-Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" `
-ContentType "application/x-www-form-urlencoded" `
-Body $tokenBody
$accessToken = $tokenResponse.access_tokenStep 2: Create the content
POST {baseUrl}/app/provisioning/content/add
Authorization: Bearer {access_token}
Content-Type: application/json
Sessionid: <optional-correlation-id>| Header | Required | Notes |
|---|---|---|
Authorization | Yes | Bearer <access_token> from Step 1. |
Content-Type | Yes | application/json. |
Sessionid | No | Free-form value used only to trace a request. |
Response
200 OK with the created item:
{
"uniqueId": "3f2c9b1a-1234-4a1b-9abc-1122334455aa",
"webRelativeUrl": "/sites/Contoso/Lists/Announcements/DispForm.aspx?ID=42"
}Errors use a standard envelope:
{
"message": "Human readable message",
"code": "SOME_ERROR_CODE",
"additionalData": null
}| Status | Meaning |
|---|---|
400 | Invalid payload or missing required field. |
401 | Missing, expired or invalid token. |
403 | Valid token, but the application does not have the access_as_application role. |
404 | Community or content type not found. |
500 | Unexpected error. Retry with backoff. |
Request body
| Member | Type | Required | Description |
|---|---|---|---|
community | object | Yes | Target community. |
fields | object | Yes | Fields of the content item (title, summary, taxonomy, and so on). |
addonId | string (GUID) | Yes | Identifier of the content type configured in your tenant. |
community
{
"community": {
"text": "Contoso",
"key": "<community-guid>",
"siteRelativeUrl": "/sites/Contoso"
}
}siteRelativeUrlis the recommended way to target a community. If present, it is used directly.- If it is omitted, the community is resolved by
key. Provide at least one of the two. textis informational only.
Common fields
| Field | Type | Notes |
|---|---|---|
title | string | Required for all content types. |
summary / description | string | Depends on the content type. |
author | object | account, loginName, email, name, entityType. Use a real user or service account of the tenant. |
category | object { key, text } | Must be an existing term. |
tags | array of { key, text } | Must be existing terms. |
departments | array of { key, text } | Must be existing terms. |
locations | array of { key, text } | Must be existing terms. |
relativeListUrl / relativeFolderPath | string | Target list or folder. It must already exist. |
togoApplyAudience / togoAudienceType | bool / string | Audience targeting. "custom" is the usual value. |
Examples by content type
Replace every GUID and URL with the real values of your tenant. The examples target a community called Contoso at /sites/Contoso.
Announcement
{
"community": {
"text": "Contoso",
"siteRelativeUrl": "/sites/Contoso"
},
"fields": {
"title": "Quarterly results are in",
"summary": "Read the highlights of this quarter's results.",
"image": {
"url": "https://<tenant>.sharepoint.com/sites/Contoso/SiteAssets/announcement.png"
},
"author": {
"account": "i:0#.f|membership|svc-content@<tenant>.onmicrosoft.com",
"loginName": "i:0#.f|membership|svc-content@<tenant>.onmicrosoft.com",
"email": "svc-content@<tenant>.onmicrosoft.com",
"name": "Content Service Account",
"entityType": "User"
},
"category": { "key": "<category-guid>", "text": "Default" },
"tags": [ { "key": "<tag-guid>", "text": "results" } ],
"relativeListUrl": "/sites/Contoso/Lists/Announcements",
"relativeFolderPath": "/",
"departments": [ { "key": "<department-guid>", "text": "Finance" } ],
"locations": [ { "key": "<location-guid>", "text": "Barcelona" } ],
"togoApplyAudience": true,
"togoAudienceType": "custom"
},
"addonId": "<announcements-addon-guid>"
}Application
{
"community": {
"text": "Contoso",
"siteRelativeUrl": "/sites/Contoso"
},
"fields": {
"title": "Expense Tool",
"description": "Submit and track your expense reports.",
"image": {
"url": "https://<tenant>.sharepoint.com/sites/Contoso/SiteAssets/expense-tool.jpg"
},
"category": { "key": "<category-guid>", "text": "Finance Tools" },
"author": {
"account": "i:0#.f|membership|svc-content@<tenant>.onmicrosoft.com",
"loginName": "i:0#.f|membership|svc-content@<tenant>.onmicrosoft.com",
"email": "svc-content@<tenant>.onmicrosoft.com",
"name": "Content Service Account",
"entityType": "User"
},
"tags": [ { "key": "<tag-guid>", "text": "finance" } ],
"togoApplicationUrl": "https://expenses.example.com",
"togoHighlight": true,
"relativeListUrl": "/sites/Contoso/Lists/Applications",
"togoSummaryDetails": "Submit and track your expense reports.",
"togoImagesDetails": [
{ "url": "https://<tenant>.sharepoint.com/sites/Contoso/SiteAssets/expense-tool-detail.jpg" }
]
},
"addonId": "<applications-addon-guid>"
}News / Page
{
"community": {
"text": "Contoso",
"siteRelativeUrl": "/sites/Contoso"
},
"fields": {
"title": "New office opening in Madrid",
"summary": "We are excited to announce our new office location.",
"category": { "key": "<category-guid>", "text": "Corporate" },
"author": {
"account": "i:0#.f|membership|svc-content@<tenant>.onmicrosoft.com",
"loginName": "i:0#.f|membership|svc-content@<tenant>.onmicrosoft.com",
"email": "svc-content@<tenant>.onmicrosoft.com",
"name": "Content Service Account",
"entityType": "User"
},
"publishingDate": "2026-01-15T00:00:00.000Z",
"dueDate": "2026-06-30T00:00:00.000Z",
"tags": [ { "key": "<tag-guid>", "text": "corporate" } ],
"relativeFolderPath": "/",
"departments": [ { "key": "<department-guid>", "text": "Corporate" } ],
"locations": [ { "key": "<location-guid>", "text": "Madrid" } ],
"togoApplyAudience": true,
"togoAudienceType": "custom"
},
"addonId": "<news-addon-guid>"
}News and pages are also scheduled automatically from publishingDate and dueDate. No extra action is needed.
Full example: token and content
$tenantId = "<TENANT_ID>"
$clientId = "<CLIENT_ID>"
$clientSecret = "<CLIENT_SECRET>"
$scope = "api://<Backend-API-app-id>/.default"
$baseUrl = "https://<your-tenant>.togo-provisioning-api.azurewebsites.net"
$tokenResponse = Invoke-RestMethod -Method Post `
-Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" `
-ContentType "application/x-www-form-urlencoded" `
-Body @{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = $scope
}
$headers = @{
Authorization = "Bearer $($tokenResponse.access_token)"
Sessionid = [guid]::NewGuid().ToString()
}
$body = Get-Content -Raw -Path ".\announcement.json"
$result = Invoke-RestMethod -Method Post `
-Uri "$baseUrl/app/provisioning/content/add" `
-Headers $headers `
-ContentType "application/json" `
-Body $body
$resultBest practices
- Store the client secret in a secure vault, never in source code.
- Cache the token and refresh it shortly before it expires.
- Send one content item per request.
- Retry
500errors with exponential backoff. - Request only the
access_as_applicationrole, on an app registration dedicated to this integration.
Troubleshooting
| Symptom | Likely cause | Action |
|---|---|---|
401 Unauthorized | Missing, expired or invalid token. | Request a new token and check client_id, client_secret, tenant_id and scope. |
403 Forbidden | The application does not have the access_as_application role. | Ask your administrator to grant the role and give admin consent. |
404 for the community | siteRelativeUrl or key does not match an existing community. | Check the community identifier with your administrator. |
400 on fields | A taxonomy term, list or folder does not exist in the target site. | Check the GUIDs and paths in the SharePoint site. |
| Content created but not visible | Audience fields restrict who can see it. | Review the audience configuration with your administrator. |