Changelog#
[0.12.1] - 2026-10-07#
Added#
Path.iter_pathstakesattributesandexcluded_attributes, and then only yields the paths a response keeps. A storage can skip loading the other ones.
Fixed#
A complex attribute returned on request is kept when
attributesholds one of its sub-attributes, as for an attribute returned by default.
[0.12.0] - 2026-10-06#
Added#
ScimPolicy.comparison_keydecides the form strings are compared under in filters, sorting and PATCH operations. It can apply the PRECIS rules RFC 7644 §5 requires foruserNameandpassword. A request that writes a string the key refuses is rejected withinvalidValue.
Changed#
Strings are mapped to lowercase instead of case folded, as PRECIS does.
Straßeno longer matchesSTRASSE.A PATCH
addno longer duplicates a value the attribute holds under another case or normalization form, and a removal by value finds it. A case-exact sub-attribute still tells two entries apart.
[0.11.0] - 2026-10-06#
Added#
Filters accept a compared sub-attribute after a value selection, as Microsoft Entra ID sends it:
emails[type eq "work"].value eq "x"reads asemails[type eq "work" and value eq "x"].AttributeBinding.comparablereturns the form a value is compared under: NFC, and case folded unless the attribute iscaseExact. A storage can keep this form to filter, sort and check uniqueness asmatch()does.
Changed#
SCIM dumps omit empty complex attributes, empty arrays, and empty entries of multi-valued attributes, as unassigned values. A PATCH that removes the last sub-attribute of
name, or a projection that selects none of them, no longer returns"name": {}. Message attributes and PATCH values are still dumped when empty.An empty array no longer satisfies a required attribute in creation and replacement requests, as RFC 7643 §2.5 makes it an unassigned value. A bulk request with no
Operationsis refused.schemaslists only the extensions present in the dump. A PATCH that removes an extension schema fromschemas, such asschemas[value eq "urn:…:enterprise:2.0:User"], removes the extension. Before, the operation reported a change but the extension stayed.
Fixed#
SearchRequest.sortnormalizes strings to NFC, as filters do. A composed and a decomposedJosénow sort together.
[0.10.6] - 2026-10-06#
Fixed#
Reference[User[EnterpriseUser]]publishesUseras its reference type, instead ofUser[EnterpriseUser].
[0.10.5] - 2026-10-06#
Added#
Path.iter_pathstakes atarget_typefilter. It yields the paths whose value is one of the given types, sub-attributes and extensions included.target_type=[Reference]yieldsmembers.$refon a group.
[0.10.4] - 2026-10-06#
Added#
Path.resolvetakes an optional model. It picks one type of a union, or resolves an unbound path.SearchRequest.sort_bindingreturns the attribute thatsort()orders by on a model, for backends that sort on their own. It returnsNonewhen the model cannot sort on it.emailsbinds asemails.value.
Fixed#
SearchRequest.start_index_0is0withoutstartIndex, andSearchRequest.stop_index_0followscountwithoutstartIndex. Both stayNonewith acursor.
[0.10.3] - 2026-10-03#
Added#
UnauthorizedException,ForbiddenException,NotFoundException,ConflictException,PreconditionFailedException,PayloadTooLargeExceptionandNotImplementedException, for the HTTP statuses of RFC7644 §3.12 that come with noscimType.
Changed#
SCIMException.from_errorpicks the exception from the status when theErrorhas no knownscimType. A404gives aNotFoundExceptioninstead of the baseSCIMException.UniquenessExceptionis a subclass ofConflictException.
[0.10.2] - 2026-10-01#
Fixed#
PatchOp.build_fromleaves out the read-only sub-attributes of multi-valued attributes, such as a read-onlymembers.display. The patch it builds is no longer rejected.
[0.10.1] - 2026-09-30#
Removed#
The
attributesandexcluded_attributesparameters ofmodel_dump()andmodel_dump_json(), deprecated in 0.8.0. Pass aResponseParametersasresponse_parametersinstead. #141
Fixed#
ListResponsereads the pagination capabilities of theScimProvider, including a provider opened around the validation. The last page of a server that only supports cursor pagination no longer needstotalResults.The policy of a
ScimProviderpassed asscim_providerapplies to the validation and the serialization. It wins over the policy of an open block.scim_policystill wins over both.
[0.10.0] - 2026-09-30#
Added#
Support for RFC9865
Resource.replacereturns whether the replacement changes the resource, the order of multi-valued entries aside. A server can keepmeta.versionandmeta.lastModifiedwhen a PUT changes nothing.In the
BULK_REQUESTcontext, an invalid operation of aBulkRequestno longer fails the whole request. It becomes its own failed result, with astatusand anError, as RFC7644 §3.7.3 requires. Under aScimProvider, each operation is read as the resource type itspathtargets. An endpoint that serves none of the types of the request fails the operation withinvalidPath.BulkOperation.endpointandBulkOperation.resource_idread the target of an operation from itspath.
Changed#
SCIMException.from_errorreconstructsInvalidCursorException,ExpiredCursorExceptionandInvalidCountExceptionfrom anErrorcarrying the matchingscimType, as RFC9865 §2.1 defines them. They used to fall back to the baseSCIMException.AttributeBinding.urnand the error messages that quote it spell the attribute as the schema declares it, such asuserName, whatever case the path used.Validation errors locate the value with an attribute path, such as
emails[1].value, and no longer expose internal names of pydantic.
Fixed#
A
ScimProviderorScimPolicyblock that exits in a context where it did not enter, such as another thread or another asyncio task, raises an error. It used to remove the provider or the policy of another block.
Security#
The
passwordof aUseris case-exact, sopassword eq "SECRET"no longer matchessecret. RFC 7643 declares it case-insensitive, but compares passwords by salted hash (RFC7643 §4.1.1), and the rules of RFC7644 §5 keep the case of passwords.A filter or a PATCH path that nests more than 32 expressions, such as
not(not(...)), is refused withinvalidFilterorinvalidPath. A deep one used to raise aRecursionError. Parentheses and chains ofandorordo not count.Filters and paths from clients no longer grow memory without limit. Each unknown attribute name, and each spelling of a known one such as
USERNAME, used to stay in memory for the life of the process. Filters and paths longer than 1024 characters are no longer cached.Write-only and never-returned attributes, such as
password, no longer appear in thereprof a model. APatchOperationbound to a resource type, as in aPatchOp[User], leaves out avaluethat writes one of them. Validation error messages no longer include the input values, whichValidationError.errors()still returns.Under
RemoveValue.apply, a PATCHremovewhosevaluehas a key that is not an attribute name, such as"value pr or value", is refused withinvalidValue.In a
BulkRequest, thedataof aPATCHoperation must be aPatchOp, and thedataof aPOSTor aPUTmust be a resource. A full resource sent as thedataof aPATCHused to be accepted with its read-only attributes, such asidandgroups. An invaliddataonly reports the errors of the type the method expects.In the
BULK_REQUESTcontext, thelocation,statusandresponseof an operation are ignored, and they are left out of a serialized request. A client can no longer make an operation look like it failed.
[0.9.0] - 2026-09-27#
Added#
ScimPolicy.unmatched_path_filtercan make a PATCHaddorreplacecreate the entry its path filter describes when no entry matches, as Microsoft Entra ID expects. By default, the operation still fails withnoTarget.
Changed#
Python 3.11 is now the minimum supported version.
The enumerations, such as
Mutability, areStrEnum:strand f-strings give their value,readOnlyrather thanMutability.read_only.In a model built from a schema, an attribute named after a member of the model, such as
copy, is held ascopy_. Its SCIM name is unchanged.A PATCH
addwhose path filter matches no entry, such asemails[type eq "work"].valueon a user without a work email, now fails withnoTargetinstead of silently doing nothing.Path.setraisesNoTargetExceptionin that case when strict.A PATCH
addorreplaceon a filtered path, such asemails[type eq "work"], merges its value into the matching entries instead of replacing them (RFC7644 §3.5.2.3). The entries are updated in place, so the immutable sub-attributes of a group member cannot be changed this way.
Fixed#
A
PatchOperationwith a null value keeps it when dumped. Areplacethat clears its target used to be sent without a value.Setting a null value under an unset complex attribute or extension no longer creates an empty one, and no longer reports the resource as modified.
A
PatchOpwith no operation, or with an operation other thanadd,removeandreplace, fails withinvalidValueinstead of a validation error withoutscimType.A PATCH
addorreplaceon a complex attribute keeps the sub-attributes its value leaves out, instead of replacing the whole attribute (RFC7644 §3.5.2.3).A PATCH
addwithout a path adds to the multi-valued attributes in its value, like anaddwith a path, instead of replacing their values.A key of a PATCH value can be an attribute path, such as
name.givenNameorurn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber, as Microsoft Entra ID and its SCIM Validator send. It used to be rejected as an undeclared attribute.Undeclared attributes and sub-attributes in a PATCH value follow
ScimPolicy.unknown.PATCH checks immutable attributes at every level and in every operation, including operations with no path, an empty path or a schema URN, and the removal of an extension. For example, the
valueof a group member can be added and removed, but not changed (RFC7643 §4.2). Setting a first value withreplace, or writing back the current value, is accepted.PATCH checks read-only attributes at every level, such as the
displayNameof the enterprisemanager. A path to one is rejected. A value that contains one is rejected only if it changes it, so an attribute sent back as it was read is accepted. Okta, for example, sends back theidof a group it renames.PATCH rejects a change to a required attribute only when it leaves the attribute unset (RFC7644 §3.5.2.2). Removing some values of a required multi-valued attribute, removing a sub-attribute of a required complex attribute, or adding an empty list is now accepted. Required sub-attributes and extensions are checked too.
A PATCH
replacewithout a value fails withinvalidValueinstead of clearing its target. So does an operation whose path targets the resource or an extension with a value that is not an object, which used to be ignored.A PATCH
replacethat sets severalprimaryentries fails withinvalidValue, likeaddalready did, instead of keeping one of them at random.PatchOp.patchraisesInvalidValueExceptionwhen the attribute rejects a value, instead of a pydanticValidationErrorwithoutscimType.PatchOp.patchno longer reports a resource as modified when an operation writes a complex or multi-valued value it already has.A PATCH path to an undeclared attribute follows
ScimPolicy.unknown, like a value does. Withignoreorkeep, the operation changes nothing instead of failing the whole patch withinvalidPath. An undeclared sub-attribute in a filter still fails withinvalidFilter.
Security#
A published schema can no longer break the models built from it, nor make
ScimProvider.from_discoveryraise another exception thanScimProviderError. Complex attributes are nested two levels deep at most, as RFC7643 §7 allows forSchema.get_model_by_payload()matches no model whenschemasis not a list of strings.
[0.8.2] - 2026-09-25#
Added#
SearchRequest.sortorders resources as RFC7644 §3.4.2.3 describes, for a server keeping them in memory.
Changed#
A filter on a write-only attribute, such as
password, only takeseq,neandpr, RFC7643 §4.1.1 comparing it for equality alone. Any other operator answersinvalidFilter.SearchRequest.sort_byrefuses a complex attribute, such asnameoraddresses, where RFC7644 §3.4.2.3 asks for a sub-attribute, a binary attribute, and a write-only attribute, such aspassword. A multi-valued attribute holding avalue, such asemails, is still sorted on it.
Fixed#
Resource.idis case-exact, as RFC7643 §3.1 declares it, so filters and orders on it respect the case.The
Metasub-attributes are read-only, andresourceTypeandversionare case-exact, as RFC7643 §3.1 declares them.Resource.replacekeeps a write-only attribute, such aspassword, that the replacement leaves out, and clears it on an explicit null only. A client never gets that value back, so a PUT built from a GET used to erase it.A model built with
Resource.from_schemaorExtension.from_schemais named as its schema, and its docstring is the description of the schema, soto_schemapublishes both again. A schema namedpetOwnerused to build aPetOwnerclass, and to lose its description.
[0.8.1] - 2026-09-25#
Changed#
Error.from_validation_errorfollows RFC7644 §3.12: an invalid value answersinvalidValue, where it used to beinvalidSyntaxor nothing, and an invalid payload structure answersinvalidSyntax.
Fixed#
from_error()keeps theErrorobject it is built from, andto_error()gives it back. The status and the scimType a server sent used to be replaced by the ones of the exception class, which dropped everything it has no class for, such as a429status or a vendor specific scimType.Validation error messages name attributes as SCIM spells them, such as
userNameinstead ofuser_name, and extensions by their schema URN.
[0.8.0] - 2026-09-20#
Added#
ScimFilterparses thefilterquery parameter and matches resources against it withmatch(). See Filter deviations from the SCIM RFC. #17PATCH paths take a value selection, such as
emails[type eq "work"].value.SearchRequestandResponseParameterstake the resource type an endpoint serves, as inSearchRequest[User]orSearchRequest[User | Group]. Theirfilter,sortBy,attributesandexcludedAttributesresolve against those models, so a misspelled attribute is caught at validation time.Path.resolveanswers theAttributeBindinga path designates.ScimFilter.quoterenders a value as a filter literal. On Python 3.14,ScimFilterandPathtake a t-string and quote what is interpolated. See Build safe SCIM filters.ScimProviderdescribes a SCIM service: the models it serves, and the objects its discovery endpoints answer.from_discovery()builds one from what a service publishes, and a service that cannot be described is refused withScimProviderError. See Describe a SCIM service. #108An extension may be declared required, as in
User[Annotated[EnterpriseUser, Required.true]]. A creation or a replacement request that leaves it out is refused. See Define custom SCIM models. #105ScimPolicystates how much a payload may depart from the specification and still be read. Name a policy at the call, or open awithblock on it or on a provider carrying one. Every setting defaults to the strict reading, so nothing changes until one is chosen. See Tolerate a non-conformant peer. #85 #108model_dump()andmodel_dump_json()take aresponse_parameters, theResponseParametersa client sent. ASearchRequestis one, so a server answeringPOST /.searchpasses the request it received. #141model_validate(),model_validate_json(),model_dump()andmodel_dump_json()take ascim_providerand ascim_spc, so that the rules the specification makes conditional on a declared capability are read. Awithblock opened on a provider lends both. See Describe a SCIM service.build_from()builds the patch turning one resource state into another. Only the attributes the wanted state names take part in the comparison. See Build a patch from two resource states. #104Bulk messages are validated, in the new
BULK_REQUESTandBULK_RESPONSEcontexts. Each operation’sdatais checked as the single request it stands for. See Bulk jobs. #149lark is a new dependency.
Changed#
Resource.from_schemaandExtension.from_schemarefuse a schema declaring two attributes whose names only differ by case, and report both. RFC7643 §2.1 #166Attribute names are matched case-insensitively, and nothing else:
{"user-name": "x"}, which 0.7 read asuserName, is now an unknown attribute thatunknowngoverns. Paths, filters andsortByresolve the same way, and a model whose fields answer to one attribute name raises aTypeErrorwhere it is defined. RFC7643 §2.1 #166The bulk models take the resource type their operations carry, as in
BulkRequest[User]orBulkRequest[User | Group], and raise aTypeErrorwhen used bare. A payload the type parameter does not cover is now refused, and a bulk response no longer dumpspath.ListResponseraises aTypeErrorwhen used without the resource type its entries carry, asPatchOpand the bulk models do. A bareListResponseused to answer a pydantic error namingResource.A message type parameter must name resource types.
ListResponse[str]used to build a class that read anything as its entries.ListResponse[Resource],PatchOp[Resource]and their bulk counterparts stay writable where a type is expected, and raise aTypeErrorwhen they read or build a payload.PatchOp[Resource]used to be refused as a type, andListResponse[Resource]used to read payloads.SearchRequest.filteris aScimFilterinstead of astr, so a malformed filter is rejected at validation time.Paths are parsed with the RFC7644 §3.5.2 grammar, so malformed ones such as
emails[oruserName ==, which used to be accepted, are now rejected.Path.modelanswers the model a path designates when it names no attribute, andNoneotherwise.resolve()answers what it used to.SearchRequest.sort_bynaming an attribute no resource type declares answersinvalidPath, where it used to be carried to the endpoint.A PATCH
removecarrying avalueis refused withinvalidValue, at validation and when applied. It used to remove the entries equal to thatvalue. Setremove_value_as_filterto keep reading it.A PATCH reaching an extension attribute takes the extended resource type, as in
PatchOp[User[EnterpriseUser]].PatchOp[User]used to carry such an operation to the endpoint, and now refuses a path its type parameter leaves out.
Removed#
Resource.get_by_schemaandResource.get_by_payload, deprecated in 0.6.13. Useget_model_by_schema()andget_model_by_payload(), whoseresource_typesparameter is namedmodels.The
originalparameter ofmodel_validate(), deprecated in 0.6.7. Validate the payload, then callreplace()on the result to compare it against the stored resource: an immutable attribute that changed raisesMutabilityExceptionfromreplaceinstead of aValidationErrorfrommodel_validate.Path.field_name,Path.field_type,Path.is_multivalued,Path.get_annotationandPath.urn. Useresolve(), whoseAttributeBindingcarries them.Path.is_prefix_ofandPath.has_prefix. They compared the text of two paths, which anything between brackets defeated.
Deprecated#
The
attributesandexcluded_attributesparameters ofmodel_dump()andmodel_dump_json(). Pass aResponseParametersasresponse_parametersinstead; naming both raises aTypeError. They will be removed in 0.9.0. #141
Fixed#
A PATCH operation carrying no
paththat unassigns an extension declaredRequired.trueis refused. #166A filter or a path accepts a
$anywhere in an attribute name, asnameCharallows. #166A schema declaring several attributes that yield one Python name builds a field for each of them: the attribute already spelled as that name keeps it, and the others are held under their SCIM name. An attribute is read under the name SCIM gives it, as in
resource["employeeId"]. #166A pydantic error spells the attribute as SCIM does,
userNameand$ref, and so does the JSON schema a model publishes. #166A field declaring its own
aliasis read under it, and an unknown attribute is refused under the spelling the peer used. #166An extension is read under its class name as well as under its URN, so
User[EnterpriseUser](EnterpriseUser=extension)is accepted and a resource carrying an extension survives a dump without aliases read back. #166A bulk model indexed with something other than a resource type names itself in the error, where
BulkRequest[str]used to tell the caller to writePatchOp[User].A subclass of a parameterized message, such as
class Users(ListResponse[User]), reads its payloads with the type parameter it inherits. It used to raise anIndexError.A PATCH operation targeting an attribute of an extension answers for the constraints that extension declares, where it used to look them up on the resource and find none. A refused operation no longer leaves the extension instantiated on the resource.
A PATCH operation carrying no
pathaccepts a resource as itsvalue, and checks the attributes it names against the model. They used to go through unexamined.A PATCH operation whose
pathnames an attribute the resource schema does not declare is refused withinvalidPath. #164A refused PATCH
addon a multi-valued attribute leaves the attribute as it was. The entry used to be appended before being validated, and outlived the failure.A PATCH operation carrying no
pathmarks the attributes it assigns as set, soexclude_unsetdumps them.A PATCH
replaceof aRequired.trueattribute with a null value or an empty array is refused, and an operation leaving such an attribute unassigned answersinvalidValueinstead ofmutability.Attributes annotated
read_onlyare left out of a payload dumped in theRESOURCE_PATCH_REQUESTcontext, as they already are in a creation or a replacement request.Paths resolve against the attributes a model declares instead of the values a resource carries, so
name.unknownis refused on a resource carrying noname, anduserName.fooanswersinvalidPathinstead of raising anAttributeError.A path crossing a multi-valued attribute, such as
emails.value, reads, writes and removes the sub-attribute of every entry, where it used to raise anAttributeErroror do nothing.A path names a
$refsub-attribute under that spelling, as inmembers.$ref, both when it is parsed and initer_paths().A schema URN carries its attribute behind a colon, so
urn:ietf:params:scim:schemas:core:2.0:UserIdno longer reads as theidof aUser, and a URN that is neither the schema of the model nor that of one of its extensions designates nothing.Resource.replacechecks the immutable and read-only sub-attributes of the entries a multi-valued attribute keeps.Resource.to_schemapublishes the members of a string enumeration ascanonicalValues, and describes the resource alone onUser[EnterpriseUser], where it used to publish the extension URN as an attribute of theUserschema.GroupMember.displayisreadWrite, as RFC7643 §8.7.1 declares it.Subscribing a
Pathto a model, and parameterizing a resource with an extension, no longer keep those classes alive for as long as the process runs. A server building a model per schema it discovers used to accumulate every one of them.
[0.7.0] - 2026-09-05#
Added#
get_model_by_schema()andget_model_by_payload()look a model up among any sequence ofScimObjectsubclasses, and reflect the input types in the returned type.ScimObjectandAnyScimObjectare exposed in the public API, so a downstream project can annotate a value that is either a resource or a message.ExtensibleStringEnumis exposed in the public API, so a custom model can suggest canonical values without restricting them.model_validate_json()takes ascim_ctxparameter, like the other validation and serialization methods, so a JSON payload can be validated without being decoded first. #150
Changed#
Pathsubclassesstrinstead ofUserString, so a path goes wherever a string does. Its string operators answer a plainstr:Path("emails.value")[6:]answers".value"where it used to answerinvalidPath.A malformed
PathanswersinvalidPathinstead ofinvalidSyntax, and building one directly raisesInvalidPathExceptioninstead of a plainValueError. A model holding one still raises aValidationError, now naming the field and carrying thescimType.SearchRequest.sort_by,attributesandexcluded_attributesrefuse a path selecting values, such asemails[type eq "work"]. The attribute notation of RFC7644 §3.10 names an attribute, not the values it holds.model_dumpandmodel_dump_jsonmove fromScimObjecttoBaseModel, so every model is dumped in a SCIM context by default:Name(family_name="Doe").model_dump()returns{"familyName": "Doe"}instead of{"family_name": "Doe"}.model_dump()withscim_ctx=Nonereturns the native pydantic dump,Nonevalues included, as its documentation states. Passexclude_none=Trueto get the former output.replace()leaves the fields it copies from the original resource unset, somodel_fields_setonly holds the attributes the client asserted.Attributes suggesting canonical values accept values outside that set, and match it case-insensitively:
Email(type="WORK").typeisEmail.Type.work.str()on them answers the SCIM value instead of the enum representation, and their JSON schema advertises the set asexamplesrather than a restrictiveenum. This covers thetypeofEmail,PhoneNumber,Im,Photo,AddressandAuthenticationScheme. #34The
schemasattribute is built from the model definition on serialization. It holds what a peer asserted otherwise, and is empty when the payload omitted it, so the omission stays visible inmodel_fields_set. It is not subject to attribute filtering anymore.A resource omitting its
schemasattribute is read instead of being rejected, which covers the partial responses of RFC 7644 §3.4.3. Its type comes from theListResponseparameter, so a response holding several resource types still cannot tell what an unlabelled resource is. #20A
schemasattribute that does not contain the model base schema is rejected whatever the validation context. It used to be accepted without a SCIM context.
Fixed#
The
readOnly,immutableandrequiredconstraints of a PATCH operation are checked against the attribute its path resolves to, instead of a literal field name match. They used to be skipped whenever the two differed, souserNamecould be removed and a path spelledGROUPScould write to areadOnlyattribute.replace()checks the constraints an extension declares, recursively.referenceandbinaryattributes are case-exact, unless a schema states otherwise. RFC 7643 §2.3.6 and §2.3.7, erratum 6001ResourceTypeendpointand thevalueofGroupMemberandGroupMembershipare case-exact, the latter holding resourceidvalues. RFC 7643 errata 8475 and 8472from_schema()reads areferenceattribute missing the optionalreferenceTypesas aURIreference, instead of crashing.Looking a model up by schema no longer crashes when the model list mixes resources with messages such as
ListResponse.
Removed#
Error.make_*_error()class methods, deprecated in 0.6.0. Use the matchingSCIMExceptionsubclass and itsto_error()method instead.The
ExternalReferenceandURIReferencealiases, deprecated in 0.6.0. UseExternalandURIinstead.The
Reference[Literal["X"]]syntax, deprecated in 0.6.0. UseReference["X"]instead.Defining a model schema with a
schemasdefault value, deprecated in 0.6.0. Use__schema__ = URN("...")instead. Note that__schema__only accepts valid URNs, while the removed syntax silently ignored invalid ones.
Deprecated#
Resource.get_by_schemaandResource.get_by_payloadare deprecated in favor ofget_model_by_schema()andget_model_by_payload(). Theirresource_typesparameter is namedmodelsin the new functions. They will be removed in 0.8.0.
Performance#
Validation and serialization each run through a single model validator and serializer, where every SCIM context used to add its own.
Commonly used field metadata is cached on
__scim_info__, and so is attribute name normalization.
[0.6.12] - 2026-04-13#
Added#
Compatibility with Pydantic 2.13.
[0.6.11] - 2026-04-10#
Added#
add uniqueness, returned and case_exact filters to iter_paths
[0.6.10] - 2026-04-07#
Fixed#
replace copies readOnly and preserves immutable fields
[0.6.9] - 2026-04-07#
Added#
*RequestContextand*ResponseContextgeneric type aliases that wrapSCIMValidatorandSCIMSerializerfor each SCIM context (e.g.CreationRequestContext[User],CreationResponseContext[User]).
[0.6.8] - 2026-04-03#
Added#
SCIMValidatorandSCIMSerializerPydantic Annotated markers to inject a SCIMContextduring validation and serialization. #130MutabilityExceptionhandler in framework integration examples (FastAPI, Flask, Django).
Deprecated#
The
originalparameter ofmodel_validate()is deprecated. Usereplace()on the validated instance instead. Will be removed in 0.8.0.
Fixed#
PATCH operations on
immutablefields are now validated at runtime per RFC 7644 §3.5.2:addis only allowed when the field has no previous value,replaceis only allowed with the same value, andremoveis only allowed on unset fields.
[0.6.7] - 2026-04-02#
Added#
ListResponsemodel_dumpandmodel_dump_jsonnow acceptattributesandexcluded_attributesparameters. #59New
ResponseParametersmodel for RFC7644 §3.9attributesandexcludedAttributesquery parameters.SearchRequestinherits from it.ResponseParametersandSearchRequestaccept comma-separated strings forattributesandexcludedAttributes.
[0.6.6] - 2026-03-12#
Fixed#
Fix ListResponse.totalResults validation when resources is none. #133
[0.6.5] - 2026-03-10#
Fixed#
Fix extension serialization crash when an extension is declared but not populated on a resource serialized outside of SCIM context (e.g. FastAPI
response_model). #131
[0.6.4] - 2026-02-05#
Added#
SCIMExceptionnow accepts an optionalscim_ctxparameter to indicate the SCIM context in which the exception occurred.
[0.6.3] - 2026-01-29#
Fixed#
Fix
model_json_schema()generation for models containingReferenceorPathfields. #125Group
displayNameis required. RFC 7643 erratum 5368 #123 #128GroupMembership$refonly referencesGroup. RFC 7643 erratum 8471Managervalueis case-exact. RFC 7643 erratum 8472ResourceTypenameandendpointhave server uniqueness. RFC 7643 erratum 8475Complex attributes don’t have
uniquenessin schema representation. RFC 7643 erratum 6004
[0.6.2] - 2026-01-25#
Added#
SCIMException.from_errorto create an exception from a SCIMErrorobject.
[0.6.1] - 2026-01-25#
Added#
Allow
Pathobjects in Pydantic validation methods.
[0.6.0] - 2026-01-25#
Added#
Changed#
Referencetype parameters simplified:Reference[ExternalReference]→Reference[External]Reference[URIReference]→Reference[URI]Reference[Literal["User"]]→Reference["User"]Reference[Literal["User"] | Literal["Group"]]→Reference[Union["User", "Group"]]
Referencenow validates URI format forExternalandURItypes.Referenceinherits fromstrdirectly instead ofUserString.
Fixed#
Only allow one primary complex attribute value to be true. #10
Deprecated#
Defining
schemaswith a default value is deprecated. Use__schema__ = URN("...")instead.Error.make_*_error()methods are deprecated. Use<Exception>.to_error()instead.Reference[Literal["X"]]syntax is deprecated. UseReference["X"]instead. Will be removed in 0.7.0.ExternalReferencealias is deprecated. UseExternalinstead. Will be removed in 0.7.0.URIReferencealias is deprecated. UseURIinstead. Will be removed in 0.7.0.Validation that the base schema is present in
schemasduring SCIM context validation.Validation that extension schemas are known during SCIM context validation.
Introduce SCIM exceptions hierarchy (
SCIMExceptionand subclasses) corresponding to RFC 7644 error types. #103Error.from_validation_errorto convert PydanticValidationErrorto SCIMError.PatchOp.patchauto-excludes otherprimaryvalues when setting one toTrue. #116
[0.5.2] - 2026-01-22#
Fixed#
Sub-attributes of requested complex attributes are now included in responses. #114
[0.5.1] - 2025-11-07#
Added#
Support for Python 3.14.
Compile regexes.
Removed#
Support for Python 3.9.
[0.5.0] - 2025-08-18#
Added#
Validation that forbid
PatchOpwith zerooperations.
Fixed#
[0.4.2] - 2025-08-05#
Fixed#
The library is 100% typed with mypy strict.
[0.4.1] - 2025-07-23#
Fixed#
Allow
TypeVaras type parameters forPatchOp.
[0.4.0] - 2025-07-23#
Added#
Proper path validation for
attributes,excluded_attributesandsort_by.Implement
patch()
Fixed#
[0.3.7] - 2025-07-17#
Fixed#
All non strict mypy type annotations are fixed.
[0.3.6] - 2025-07-02#
Added#
Fix
ResourceType.from_resourceusage for resources with several extensions. #95
[0.3.5] - 2025-06-05#
Added#
Fix dynamic schema generation for user defined classes with inheritance.
[0.3.4] - 2025-06-05#
Added#
Implement User and Group attributes types shortcuts to match dynamically created model types.
[0.3.3] - 2025-05-21#
Fixed#
User class typing. #92
[0.3.2] - 2025-03-28#
Fixed#
Pydantic warning.
[0.3.1] - 2025-03-07#
Fixed#
Fix
start_indexandcountlimits. #84total_resultsis required. #88
[0.3.0] - 2024-12-11#
Added#
Attribute.get_attributecan be called with brackets.
Changed#
Add a
originalparameter tomodel_validate()mandatory forRESOURCE_REPLACEMENT_REQUEST. This original value is used to look ifimmutableparameters have mutated. #86
[0.2.12] - 2024-12-09#
Added#
Implement
Attribute.get_attribute.
[0.2.11] - 2024-12-08#
Added#
Implement
Schema.get_attribute.Implement
SearchRequest.start_index_0andSearchRequest.start_index_1.
[0.2.10] - 2024-12-02#
Changed#
The
schemaattribute is annotated withtrue.
Fixed#
Base64Bytescompatibility between pydantic 2.10+ and <2.10
[0.2.9] - 2024-12-02#
Added#
Implement
Resource.get_extension_model.
[0.2.8] - 2024-12-02#
Added#
Support for Pydantic 2.10.
[0.2.7] - 2024-11-30#
Added#
Implement
ResourceType.from_resource.
[0.2.6] - 2024-11-29#
Fixed#
Implement
model_dump_json().Temporarily set Pydantic 2.9 as the maximum supported version.
[0.2.5] - 2024-11-13#
Fixed#
model_validate()types.
[0.2.4] - 2024-11-03#
Fixed#
Python 3.9 and 3.10 compatibility.
[0.2.3] - 2024-11-01#
Added#
Python 3.13 support.
Proper Base64 serialization. #31
get_field_root_type()supportsUnionType.
Changed#
SearchRequest.attributesandSearchRequest.attributesare mutually exclusive. #19
[0.2.2] - 2024-09-20#
Fixed#
ListResponsepydantic discriminator issue introduced with pydantic 2.9.0. #75Extension payloads are not required on response contexts. #77
[0.2.1] - 2024-09-06#
Fixed#
[0.2.0] - 2024-08-18#
Fixed#
Note
schema.make_model() becomes Resource.from_schema(schema) or Extension.from_schema(schema).
Changed#
Enable pydantic
validate_assignmentoption. #54
[0.1.15] - 2024-08-18#
Added#
Add a PEP561
py.typedfile to mark the package as typed.
Fixed#
Changed#
Remove
ListResponseofmethod in favor of regular type parameters.
Note
ListResponse.of(User) becomes ListResponse[User] and ListResponse.of(User, Group)`` becomes ListResponse[Union[User, Group]].
ReferenceuseLiteralinstead oftyping.ForwardRef.
Note
pet: Reference["Pet"] becomes pet: Reference[Literal["Pet"]]
[0.1.14] - 2024-07-23#
Fixed#
get_by_payload return
Noneon invalid payloadsinstance
model_dump()with multiple extensions #57
[0.1.13] - 2024-07-15#
Fixed#
Schema dump with context was broken.
scim2_models.PatchOperation.opattribute is case insensitive to be compatible with Microsoft Entra. #55
[0.1.12] - 2024-07-11#
Fixed#
Additional bugfixes about attribute case sensitivity #45
Dump was broken after sub-model assignments #48
Extension attributes dump were ignored #49
ListResponsetolerate any schema order #50
[0.1.11] - 2024-07-02#
Fixed#
Attributes are case insensitive #39
[0.1.10] - 2024-06-30#
Added#
[0.1.9] - 2024-06-29#
Added#
Referencetype parameters represent SCIM ReferenceType
Fixed#
countandstart_indexvalidators supportsNonevalues.
[0.1.8] - 2024-06-26#
Added#
Dynamic pydantic model creation from SCIM schemas. #6
Changed#
Fix#
[0.1.7] - 2024-06-16#
Added#
countvalue is floored to 1start_indexvalue is floored to 0resourcesmust be set whentotal_resultsis non-null.
Fix#
Add missing default values. #33
[0.1.6] - 2024-06-06#
Added#
Changed#
Refactor
get_field_mutabilityandget_field_returnabilityinget_field_annotation.
[0.1.5] - 2024-06-04#
Fix#
[0.1.4] - 2024-06-03#
Fix#
ServiceProviderConfigurationid is optional.
[0.1.3] - 2024-06-03#
Changed#
Rename
ServiceProviderConfigurationtoServiceProviderConfigto match the RFCs naming convention.
[0.1.2] - 2024-06-02#
Added#
Implement
Resource.guess_by_payload
[0.1.1] - 2024-06-01#
Changed#
Pre-defined errors are not constants anymore
[0.1.0] - 2024-06-01#
Added#
Initial release