Skip to main content

Command Palette

Search for a command to run...

SharePoint REST API - Working with List Content Types

Updated
18 min readView as Markdown
SharePoint REST API - Working with List Content Types
R

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:

  • AllowContentTypes is a read-only-after-creation capability flag indicating whether the list supports content types. For standard generic lists and document libraries, this is true.

  • ContentTypesEnabled is 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 is false on a newly created generic list. Despite what Microsoft guidance commonly states, ContentTypesEnabled is not enforced as a prerequisite by AddAvailableContentType — in SharePoint Online testing, the operation succeeded on a list where ContentTypesEnabled was false (validated against SharePoint Online with both delegated AllSites.Manage and app-only Sites.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 SPHttpClient instead — 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: AddAvailableContentType does not require ContentTypesEnabled to be true. In testing against SharePoint Online, the operation succeeded on a list where ContentTypesEnabled was false. ContentTypesEnabled is not enforced as a prerequisite by AddAvailableContentType (validated against SharePoint Online with both delegated AllSites.Manage and app-only Sites.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 returns 200 OK with body {"@odata.null": true} rather than a 404 (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 StringId from 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) returns 200 OK with 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 500 with 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: 0 is required. SharePoint Online returns 411 Length Required without it. Most HTTP clients add it automatically, but it must be set explicitly in some environments.


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.

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.

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
    }
  ]
}

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 FieldInternalName must already exist on the list. If it does not, SharePoint returns 400 Bad Request with "Column does not exist."


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: Reorder returns 204 No Content, but a subsequent GET on the FieldLinks collection returns the same order as before the call — the new order is not reflected in the REST response (validated against SharePoint Online with both delegated AllSites.Manage and app-only Sites.Manage.All). This confirms only that the requested order is not reflected by the REST FieldLinks collection; it does not establish that SharePoint ignored the reorder for all other representations or in the UI.


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.Manage and app-only Sites.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 Manage or FullControl role rather than Write.


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:

  • AllowContentTypes and ContentTypesEnabled are separate properties. AllowContentTypes is a read-only-after-creation capability flag indicating whether the list supports content types. ContentTypesEnabled is the mutable list setting that specifies whether content types are enabled for the list. In SharePoint Online testing, AddAvailableContentType succeeded while ContentTypesEnabled was false, demonstrating that this property is not an API prerequisite for the operation (validated against SharePoint Online).

  • AddAvailableContentType takes 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 returns 200 OK with 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.

  • Reorder returns 204 but the new order is not reflected in a subsequent FieldLinks GET response.

Happy coding!