SharePoint REST API - Working with List Content Types

I'm a .NET/M365 developer, trainer, author, MVP & MCT Alumni
This post covers the list-scoped content type endpoint in SharePoint Online. List content types are scoped copies of site content types applied to a specific list. They live at:
https://<tenant>.sharepoint.com/sites/<site>/_api/web/lists(guid'<listId>')/contenttypes
Introduction
Content type IDs in SharePoint are hierarchical: a child content type's ID is formed by appending a hex suffix to its parent's ID. The built-in System content type (0x) is the root of the hierarchy. Item (0x01) derives from System and is the base for most item-based content types — a direct Item child might have a site-level ID like 0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D98.
When you add a site content type to a list using AddAvailableContentType, SharePoint creates a list-scoped copy with a new ID: the original site content type ID followed by 00 and a GUID without hyphens. For example, if the site content type ID is 0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D98, the list-scoped copy's ID might be 0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789. The Scope property on a list-scoped content type reflects the list's server-relative URL rather than the site URL. Built-in content types such as Item and Folder also receive list-scoped IDs on new lists rather than retaining their plain short IDs.
A list content type is a local copy of its parent site content type, not a live alias. Modifications made at the list level do not flow back to the parent site content type. Field-link changes to a parent site content type can be propagated down to derived list content types explicitly using update(updateChildren=true) on the site content type; property changes such as Name, Description, and Group do not propagate.
Two separate list properties control content type behavior and are often confused:
AllowContentTypesis a read-only-after-creation capability flag indicating whether the list supports content types. For standard generic lists and document libraries, this istrue.ContentTypesEnabledis the mutable list setting that specifies whether content types are enabled for the list. Among its visible effects are the content-type-related options exposed through the SharePoint UI. It isfalseon a newly created generic list. Despite what Microsoft guidance commonly states,ContentTypesEnabledis not enforced as a prerequisite byAddAvailableContentType— in SharePoint Online testing, the operation succeeded on a list whereContentTypesEnabledwasfalse(validated against SharePoint Online with both delegatedAllSites.Manageand app-onlySites.Manage.All).
Things you can do with list content types via the REST API:
Enable content types on a list (set
ContentTypesEnabled = true)List all content types associated with a list, including its template-provisioned default content type and any additional content types added to it
Read a single list-scoped content type by its full list-scoped ID
Add a site content type to a list
Update a list-scoped content type's properties
Remove a content type from a list
Manage field links on a list-scoped content type
Authentication note: The raw HTTP examples use a SharePoint Bearer token. With delegated authentication, the token represents a signed-in user and the operation must be permitted by both the delegated scope and that user's SharePoint permissions. With application (app-only) authentication, the token represents the Entra application and SharePoint evaluates its application permissions. For Entra app-only access to SharePoint REST, Microsoft documents certificate-based authentication; client-secret app-only tokens are not accepted by SharePoint REST. The legacy SharePoint ACS app-only model was retired on November 27, 2023 and stopped working on April 2, 2026. In SPFx, use
SPHttpClientinstead — it supplies the current user's SharePoint authentication context and manages request digests for write operations.
Before You Start: Headers and JSON Format
SharePoint's REST API defaults to OData v3 in most cases when the OData-Version header is not supplied. To use OData v4 — which changes how type annotations are expressed and is what SPFx's SPHttpClient uses by default — include the OData-Version: 4.0 header on your requests.
The Accept header tells the API what JSON format to return. The Content-Type header (on requests with a body) tells the API what JSON format you're sending. All examples in this post use application/json;odata.metadata=none, which returns clean JSON with no OData metadata noise.
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Content-Type: application/json;odata.metadata=none (JSON-body requests only)
For a full explanation of OData versions, JSON format options, and how these headers interact, see Understanding SharePoint REST JSON Formats.
The SP.ContentType Entity
List-scoped content types use the same SP.ContentType entity as site content types. The properties most relevant to list-scoped content types are:
| Property | Type | Notes |
|---|---|---|
StringId |
string |
For a content type added with AddAvailableContentType, the list-scoped ID consists of the site content type ID plus 00 and a GUID without hyphens. Built-in content types such as Item and Folder also receive list-scoped IDs rather than retaining their plain short IDs. |
Id |
object | {"StringValue": "0x..."} — same value as StringId wrapped in an object. |
Name |
string |
The display name copied from the site content type when it is added. Later site-level property changes do not update this list-scoped copy. |
Description |
string |
The description copied from the site content type when it is added. Later site-level property changes do not update this list-scoped copy. |
Group |
string |
The content type group copied from the site content type when it is added. Later site-level property changes do not update this list-scoped copy. |
Scope |
string |
Server-relative URL of the list (e.g. /sites/marketing/Lists/Contracts), not the site. |
API Operations
The examples below are independent — each one can be run on its own against any list with the appropriate content types present.
Enable Content Types on a List
ContentTypesEnabled specifies whether content types are enabled for the list. Setting it to true also exposes the content-type-related options in the list settings UI and is the normal configuration when users will work with multiple content types. This is a MERGE on the list endpoint, not the content types endpoint.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Content-Type: application/json;odata.metadata=none
X-HTTP-Method: MERGE
If-Match: *
{
"ContentTypesEnabled": true
}
Response — 204 No Content
Note:
AddAvailableContentTypedoes not requireContentTypesEnabledto betrue. In testing against SharePoint Online, the operation succeeded on a list whereContentTypesEnabledwasfalse.ContentTypesEnabledis not enforced as a prerequisite byAddAvailableContentType(validated against SharePoint Online with both delegatedAllSites.Manageand app-onlySites.Manage.All).
List All List Content Types
Returns all content types associated with the list. The exact built-in content types depend on the list or library template.
GET https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Response — 200 OK (abridged — example from a generic list with one custom content type added)
{
"value": [
{
"StringId": "0x0100925F65FE6A4149EFAE1E65D5DF11BD0A",
"Name": "Item",
"Group": "_Hidden",
"Scope": "/sites/marketing/Lists/Contracts"
},
{
"StringId": "0x01200006A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4",
"Name": "Folder",
"Group": "_Hidden",
"Scope": "/sites/marketing/Lists/Contracts"
},
{
"StringId": "0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789",
"Name": "Contract",
"Group": "Contoso Content Types",
"Scope": "/sites/marketing/Lists/Contracts"
}
]
}
Get a List Content Type
Retrieves a single list-scoped content type by its full list-scoped ID.
GET https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Response — 200 OK (abridged)
{
"StringId": "0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789",
"Id": {
"StringValue": "0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789"
},
"Name": "Contract",
"Description": "A contract document",
"Group": "Contoso Content Types",
"Hidden": false,
"ReadOnly": false,
"Sealed": false,
"Scope": "/sites/marketing/Lists/Contracts"
}
Note: The full list-scoped ID must be used. If you pass the original site content type ID (without the
00+ GUID suffix), SharePoint returns200 OKwith body{"@odata.null": true}rather than a404(validated against SharePoint Online).
Add a Site Content Type to a List
Adds a site content type to the list using the AddAvailableContentType action. Pass the site content type ID — SharePoint creates the list-scoped copy and returns it in the response.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes/AddAvailableContentType
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Content-Type: application/json;odata.metadata=none
{
"contentTypeId": "0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D98"
}
Response — 200 OK (abridged) — the full SP.ContentType entity representing the new list-scoped copy.
{
"StringId": "0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789",
"Id": {
"StringValue": "0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789"
},
"Name": "Contract",
"Group": "Contoso Content Types",
"Scope": "/sites/marketing/Lists/Contracts"
}
Note: Store the
StringIdfrom the response — this is the list-scoped content type ID you need for GET, MERGE, and DELETE operations on this list content type.
Note: The site content type must be compatible with the target list or library. Content types added to a document library must derive from the built-in Document content type; content types added to an ordinary list must not derive from Document. The built-in Folder content type and its derivatives are an exception — they can be used with both lists and document libraries.
Update a List Content Type
Updates a list-scoped content type's properties using the MERGE tunnel pattern. Changes apply only to the list-scoped copy and do not affect the parent site content type.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Content-Type: application/json;odata.metadata=none
X-HTTP-Method: MERGE
{
"Description": "An executed contract document"
}
Response — 204 No Content
Remove a Content Type from a List
Removes a content type from the list using the DELETE tunnel pattern. Use the list-scoped content type ID returned by AddAvailableContentType.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
X-HTTP-Method: DELETE
Content-Length: 0
Response — 204 No Content
Note: Using the original site content type ID (without the
00+ GUID suffix) returns200 OKwith body{"@odata.null": true}— no error is returned, but no content type is removed (validated against SharePoint Online).
Note: A content type can be removed from a list only when no list items are currently assigned to it. Items in the Recycle Bin do not block deletion; if later restored, SharePoint assigns them the list's default content type.
Note: The last remaining content type on a list cannot be deleted. Attempting to do so returns
500with the error "The last content type on a list cannot be deleted." Built-in content types such as Item can be deleted from an empty list — the constraint is that list items must not be assigned to the content type, not that the content type is built-in (validated against SharePoint Online).
Note:
Content-Length: 0is required. SharePoint Online returns411 Length Requiredwithout it. Most HTTP clients add it automatically, but it must be set explicitly in some environments.
Field Link Operations
Field links are the columns associated with a list-scoped content type. Each entry in the FieldLinks collection represents one column that is part of the content type's schema. The FieldLinks endpoint on a list content type supports more operations than its site content type counterpart: it can add a field link for any column that already exists on the list, in addition to customizing Hidden and Required.
The SP.FieldLink Entity
| Property | Type | Notes |
|---|---|---|
Id |
Guid |
The unique identifier of the column this field link points to. |
Name |
string |
The column's internal name. The most reliable identifier when reading field links. |
DisplayName |
string |
Returns an empty string on GET in SharePoint Online testing — use Name to identify the column. |
FieldInternalName |
string |
Returns an empty string on GET in SharePoint Online testing. It is the operative property when targeting a column in a POST body. |
Hidden |
bool |
Whether the column is hidden in list forms for this content type. |
Required |
bool |
Whether the column is required for this content type. |
List Field Links
Returns all field links for the list-scoped content type.
GET https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')/FieldLinks
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Response — 200 OK
{
"value": [
{
"Id": "c042a256-787d-4a6f-8a8a-cf6ab767f12d",
"Name": "ContentType",
"DisplayName": "",
"FieldInternalName": "",
"Hidden": false,
"Required": false
},
{
"Id": "fa564e0f-0c70-4ab9-b863-0177e6ddd247",
"Name": "Title",
"DisplayName": "",
"FieldInternalName": "",
"Hidden": false,
"Required": true
}
]
}
Add a Field Link
Adds a column to the list content type's field links. The column must already exist on the list before it can be added as a field link. If the column is not yet on the list, add it first — for example, using POST /_api/web/lists(guid'{listId}')/fields/createfieldasxml — and then add the field link.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')/FieldLinks
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Content-Type: application/json;odata.metadata=none
{
"FieldInternalName": "ContractDate",
"Hidden": false,
"Required": false
}
Response — 200 OK — the SP.FieldLink entity for the added field link (validated with both delegated and app-only authentication). Unlike the site content type FieldLinks endpoint, both authentication types return 200 OK for list content types.
Note: The column identified by
FieldInternalNamemust already exist on the list. If it does not, SharePoint returns400 Bad Requestwith "Column does not exist."
Reorder Field Links
Sets the display order of columns in the content type. Pass all field internal names in the desired order.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')/FieldLinks/Reorder
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
Content-Type: application/json;odata.metadata=none
{
"internalNames": ["ContractDate", "Title", "ContentType"]
}
Response — 204 No Content
Note:
Reorderreturns 204 No Content, but a subsequent GET on theFieldLinkscollection returns the same order as before the call — the new order is not reflected in the REST response (validated against SharePoint Online with both delegatedAllSites.Manageand app-onlySites.Manage.All). This confirms only that the requested order is not reflected by the RESTFieldLinkscollection; it does not establish that SharePoint ignored the reorder for all other representations or in the UI.
Attempt to Delete a Field Link
The REST endpoint accepts the DELETE tunnel pattern for a list-scoped field link, but in SharePoint Online testing the operation was silently ignored.
POST https://contoso.sharepoint.com/sites/marketing/_api/web/lists(guid'a1b2c3d4-0001-0001-0001-000000000001')/contenttypes('0x0100CDBF9DC95E6E4F46B2B3E56D5C0E4D9800A4B93C2D88E14F5C9A1B2D3E4F56789')/FieldLinks(guid'6e8f95a2-4b3c-4d21-9e1f-2a8c7b4e3f51')
OData-Version: 4.0
Accept: application/json;odata.metadata=none
Authorization: Bearer <token>
X-HTTP-Method: DELETE
Content-Length: 0
Response — 204 No Content
Note: In testing, deletion was silently ignored for every field link tested — the response is 204 No Content but the field link remains. This included both built-in field links (ContentType, Title) and custom field links added via REST (validated against SharePoint Online with both delegated
AllSites.Manageand app-onlySites.Manage.All).
Quick Reference
Headers
| Header | Notes |
|---|---|
Authorization: Bearer <token> |
Required for the raw HTTP OAuth examples in this post. When using SPHttpClient in SPFx, authentication is handled automatically. |
Accept: application/json;odata.metadata=none |
Required if you want a JSON response. Omitting it causes SharePoint to return Atom XML. |
Content-Type: application/json;odata.metadata=none |
Required on JSON-body requests (POST, MERGE). |
OData-Version: 4.0 |
Activates OData v4 behavior. |
X-HTTP-Method: MERGE |
Tunnels an update over POST. |
X-HTTP-Method: DELETE |
Tunnels a delete over POST. |
If-Match: * |
Required for the list-level MERGE (e.g., ContentTypesEnabled), where SharePoint's documented ETag concurrency model applies to lists and list items. Not required for content type or field link MERGE and DELETE operations — SharePoint Online accepts those requests without it (validated against SharePoint Online). |
Content-Length: 0 |
Required on bodyless DELETE tunnel requests — SharePoint Online returns 411 Length Required without it (validated). Most HTTP clients add it automatically. |
Response Status Codes
| Operation | Status | Body |
|---|---|---|
| GET list content types or single content type | 200 OK | JSON |
| GET with site-level content type ID (not list-scoped ID) | 200 OK | {"@odata.null": true} |
| DELETE with site-level content type ID (not list-scoped ID) | 200 OK | {"@odata.null": true} |
Add site content type to list (AddAvailableContentType) |
200 OK | Full SP.ContentType entity (list-scoped) |
| GET field links | 200 OK | JSON |
Add field link (POST /FieldLinks) |
200 OK | SP.FieldLink entity |
| Enable content types on list (MERGE) | 204 No Content | Empty |
| Update list content type (MERGE) | 204 No Content | Empty |
Reorder field links (/FieldLinks/Reorder) |
204 No Content | Empty |
| Remove content type from list (DELETE tunnel) | 204 No Content | Empty |
| Attempt to delete field link (DELETE tunnel, silently ignored) | 204 No Content | Empty; field link remains |
| Remove last remaining content type | 500 | Error: "The last content type on a list cannot be deleted." |
Permission Requirements
The table below lists the minimum delegated and application permission scopes. Delegated scopes apply when calling with a Bearer token on behalf of a signed-in user; the minimum user access column shows what access the signed-in user needs when using classic SharePoint permissions and modern SharePoint group permissions. Application permissions apply when calling without a signed-in user context (app-only). The application permissions in this table are permissions on the Office 365 SharePoint Online API, not the same-named Microsoft Graph permissions. An SPFx solution using SPHttpClient does not require you to grant these delegated or application scopes to the solution; it calls SharePoint as the current user, whose SharePoint permissions govern the operation. Delegated permission scope requirements were validated against SharePoint Online using a user with site Member access. The classic permission requirements in the table come from Microsoft's documentation.
| Operation | Minimum delegated scope | Minimum user access | Minimum tenant-wide application permission |
|---|---|---|---|
| List all list content types | AllSites.Read |
Classic: Read; Group: Visitor | Sites.Read.All |
| Get a list content type | AllSites.Read |
Classic: Read; Group: Visitor | Sites.Read.All |
| List field links | AllSites.Read |
Classic: Read; Group: Visitor | Sites.Read.All |
| Enable content types on a list | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
| Add a site content type to a list | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
| Update a list content type | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
| Add a field link | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
| Reorder field links | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
| Attempt to delete a field link | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
| Remove a content type from a list | AllSites.Manage |
Classic: Edit; Group: Member | Sites.Manage.All |
Note: The application permissions above are tenant-wide grants. For least-privilege app-only access to specific site collections, the SharePoint Sites.Selected application permission can be used instead. Sites.Selected consent alone does not grant access to any site — an explicit Read, Write, Manage, or FullControl role must be assigned on each target site collection. For the write operations in this post, assign the selected site a
ManageorFullControlrole rather thanWrite.
Wrapping Up
List content types are scoped copies of site content types that SharePoint creates when you add a site content type to a list. A few things to keep in mind:
AllowContentTypesandContentTypesEnabledare separate properties.AllowContentTypesis a read-only-after-creation capability flag indicating whether the list supports content types.ContentTypesEnabledis the mutable list setting that specifies whether content types are enabled for the list. In SharePoint Online testing,AddAvailableContentTypesucceeded whileContentTypesEnabledwasfalse, demonstrating that this property is not an API prerequisite for the operation (validated against SharePoint Online).AddAvailableContentTypetakes the site content type ID and returns the new list-scoped ID (site content type ID +00+ GUID). Store that list-scoped ID — you need it for GET, MERGE, and DELETE operations on the list content type.Using the site-level content type ID (without the
00+ GUID suffix) for both GET and DELETE returns200 OKwith body{"@odata.null": true}— no error is returned, but no operation is performed.The last remaining content type on a list cannot be deleted (500 error). Built-in content types such as Item can otherwise be deleted from a list — the real constraint is that no list items may be assigned to the content type at the time of deletion.
In SharePoint Online testing, field link deletion via the REST DELETE tunnel was silently ignored for every field link tested on list-scoped content types — 204 is returned but the field link remains.
Reorderreturns 204 but the new order is not reflected in a subsequentFieldLinksGET response.
Happy coding!



