Skip to main content

APIs V3 (Preview)

The APIs described in this section are currently either in Preview stage or not implemented, yet. Before using these APIs, pleases consider the API release policy below

We Appreciate your Help

We invite you to...

The Ideas behind the New V3 APIs

The current ZITADEL GA APIs are structured around contexts like System, Admin, Management, and Auth. This structure leads to duplicate methods and makes it hard to find the right API for the right task. Especially interacting with resources from multiple organizations is cumbersome. Also, the APIs evolved over time, which lead to inconsistencies and a lack of flexibility in development.

We address these issues with the following new API categories:

The designs for the new API categories aim for the following improvements:

Service Structure

Instead of structuring the API methods around contexts, new APIs are structured around resources and settings. This means, eventually, we deprecate the old System-, Admin-, Management- and AuthAPIs in favor of User-, Action-, Language-, FeatureAPIs and so on. This change makes it easier to find the right API for the right task, especially for multi-organization resources. Also, it allows for faster development and independent versioning of the APIs.

Multitenancy Management and Consistency

To improve managing and reusing resources and settings in multitenancy scenarios, we define some rules for the new APIs:

  • Single properties from default settings are overridable (patchable) in organizations.
  • Some settings support user-defined custom properties that are also overridable in organizations.
  • Improved experience with reusing resources in multiple organizations and instances.
  • Resources are searchable over all organizations with a single call by default.

HTTP and gRPC Consistency

To make the APIs more consistent and easier to use, we follow the same patterns in all Proto files.

  • Patching is favored over updating resources and settings.
  • HTTP calls are mapped so that query parameters can be used as much as possible. We avoid the annotation body: "*".
  • For search performance, we enforce query limits.

Standard Resources

Standard resources exist in exactly one context. For example, a user is always assigned to exactly one organization. Or one SMS provider is always assigned to exactly one instance.

Standard resource methods behave like this:

  • Search request results can be scoped to a RequestContext.
  • Search request results only contain results for which the requesting user has the necessary read permissions.
  • Search requests are limited to 100 by default. The limit can be increased by the caller up to 1000 by default.
  • Resource configurations are partially updatable. With HTTP, this is done via PATCH requests. If no changes were made, the response is successful.
  • Status changes or other actions on resources with side effects are done via POST requests. Their HTTP path ends with the underscore prefixed action name. For example POST /resources/users/{id}/_unlock.

For a full proto example, have a look at the ZITADELActions service.

Reusable Resources

Reusable resources are like standard resources but can be reused in multiple contexts. For example, an external identity provider can be defined once on the instance. Each organization within this instance can then choose to use this identity provider or not.

Additionally to the methods described for standard resources, reusable have the following capabilities:

Reusable resources have the same behavior as standard resources with the following additions:

  • Reusable resources can be created in a given context level (system, instance, org).
  • For requests, that require a resource ID, no request context is needed.
  • Reusable resources are available in child contexts, even if their state is inactive.
  • The child context can control if an inherited resource should be active or inactive for itself using a state policy.
  • In child contexts, the state policy of a reused resource is inherit by default and can be changed to activate, deactivate or back to inherit.
  • In child contexts, a reused resources configuration is read-only.
  • Child contexts can read at least the following properties of reused resources:
    • ID
    • name
    • description
    • state
    • the state policy in the child context
    • sequence
    • last changed date
    • parent context
    • state in the immediate parent context.
  • By default, search queries for reused resources return all resources from the given contexts, all inherited resources and all resources defined in all children contexts.

Typically, a new resource is first designed and implemented as a non-reusable resource. If the community sees a benefit in reusing the resource in multiple contexts, reusability is added to the resource.

For a full proto example, have a look at the ZITADELIdentityProviders service.

Resource Services

All resource services by default support the following CRUD operations as described above.

  • Create
  • Read (get, search)
  • Patch (partially update, success on no changes)
  • Delete

ZITADELActions

  • Standard CRUD methods for Targets
  • Standard CRUD methods for Executions except the PutExecution method replaces the CreateExecution and PatchExecution methods

Additional to the standard CRUD methods:

  • ListAvailableExecutionServices
  • ListAvailableExecutionMethods
  • ListAvailableExecutionFunctions
action_service.proto
syntax = "proto3";

package zitadel.resources.action.v3alpha;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
import "zitadel/protoc_gen_zitadel/v2/options.proto";

import "zitadel/resources/action/v3alpha/target.proto";
import "zitadel/resources/action/v3alpha/execution.proto";
import "zitadel/resources/action/v3alpha/search.proto";
import "zitadel/resources/object/v3alpha/object.proto";

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha;action";

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Action Service";
version: "3.0-alpha";
description: "This API is intended to manage custom executions (previously known as actions) in a ZITADEL instance. It is behind the feature flag \"multitenancy_resources_api\". It will continue breaking as long as it is in alpha state.";
contact:{
name: "ZITADEL"
url: "https://zitadel.com"
email: "hi@zitadel.com"
}
license: {
name: "Apache 2.0",
url: "https://github.com/zitadel/zitadel/blob/main/LICENSE";
};
};
schemes: HTTPS;
schemes: HTTP;

consumes: "application/json";
consumes: "application/grpc";

produces: "application/json";
produces: "application/grpc";

consumes: "application/grpc-web+proto";
produces: "application/grpc-web+proto";

host: "$ZITADEL_DOMAIN";
base_path: "/resources/v3alpha";

external_docs: {
description: "Detailed information about ZITADEL",
url: "https://zitadel.com/docs"
}
security_definitions: {
security: {
key: "OAuth2";
value: {
type: TYPE_OAUTH2;
flow: FLOW_ACCESS_CODE;
authorization_url: "$CUSTOM-DOMAIN/oauth/v2/authorize";
token_url: "$CUSTOM-DOMAIN/oauth/v2/token";
scopes: {
scope: {
key: "openid";
value: "openid";
}
scope: {
key: "urn:zitadel:iam:org:project:id:zitadel:aud";
value: "urn:zitadel:iam:org:project:id:zitadel:aud";
}
}
}
}
}
security: {
security_requirement: {
key: "OAuth2";
value: {
scope: "openid";
scope: "urn:zitadel:iam:org:project:id:zitadel:aud";
}
}
}
responses: {
key: "403";
value: {
description: "Returned when the user does not have permission to access the resource.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
responses: {
key: "404";
value: {
description: "Returned when the resource does not exist.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
};

service ZITADELActions {

// Create a target
//
// Create a new target, which can be used in executions.
rpc CreateTarget (CreateTargetRequest) returns (CreateTargetResponse) {
option (google.api.http) = {
post: "/targets"
body: "target"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.target.write"
}
http_response: {
success_code: 201
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "201";
value: {
description: "Target successfully created";
schema: {
json_schema: {
ref: "#/definitions/v2CreateTargetResponse";
}
}
};
};
};
}

// Patch a target
//
// Patch an existing target.
rpc PatchTarget (PatchTargetRequest) returns (PatchTargetResponse) {
option (google.api.http) = {
patch: "/targets/{id}"
body: "target"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.target.write"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "Target successfully updated";
};
};
};
}

// Delete a target
//
// Delete an existing target. This will remove it from any configured execution as well.
rpc DeleteTarget (DeleteTargetRequest) returns (DeleteTargetResponse) {
option (google.api.http) = {
delete: "/targets/{id}"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.target.delete"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "Target successfully deleted";
};
};
};
}

// Target by ID
//
// Returns the target identified by the requested ID.
rpc GetTarget (GetTargetRequest) returns (GetTargetResponse) {
option (google.api.http) = {
get: "/targets/{id}"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.target.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200"
value: {
description: "Target successfully retrieved";
}
};
};
}

// Search targets
//
// Search all matching targets. By default, we will return all targets of your instance.
// Make sure to include a limit and sorting for pagination.
rpc SearchTargets (SearchTargetsRequest) returns (SearchTargetsResponse) {
option (google.api.http) = {
post: "/targets/_search",
body: "filters"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.target.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "A list of all targets matching the query";
};
};
responses: {
key: "400";
value: {
description: "invalid list query";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
};
};
};
};
};
}

// Put an execution to call a target or include the targets of another execution.
//
// Creates an execution for the given condition if it doesn't exists.
// Otherwise, the existing execution is updated.
rpc PutExecution (PutExecutionRequest) returns (PutExecutionResponse) {
option (google.api.http) = {
post: "/executions"
body: "execution"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.execution.write"
}
http_response: {
success_code: 201
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "201";
value: {
description: "Execution successfully created";
schema: {
json_schema: {
ref: "#/definitions/v2CreateExecutionResponse";
}
}
};
};
responses: {
key: "200";
value: {
description: "Execution successfully updated";
};
};
};
}

// Delete an execution
//
// Delete an existing execution.
rpc DeleteExecution (DeleteExecutionRequest) returns (DeleteExecutionResponse) {
option (google.api.http) = {
delete: "/executions/{id}"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "action.execution.delete"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "Execution successfully deleted";
};
};
};
}

// Search executions
//
// Search all matching executions. By default, we will return all executions of your instance.
// Depending on the ZITADEL configuration, the number of returned resources is most probably limited.
// To make sure you get deterministic results, sort and paginate by the resources creation dates.
rpc SearchExecutions (SearchExecutionsRequest) returns (SearchExecutionsResponse) {
option (google.api.http) = {
post: "/executions/_search"
body: "filters"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "execution.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "A list of all executions matching the query";
};
};
responses: {
key: "400";
value: {
description: "invalid list query";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
};
};
};
};
};
}

// List all available functions
//
// List all available functions which can be used as condition for executions.
rpc ListAvailableExecutionFunctions (ListAvailableExecutionFunctionsRequest) returns (ListAvailableExecutionFunctionsResponse) {
option (google.api.http) = {
get: "/executions/functions"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "execution.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "List all functions successfully";
};
};
};
}
// List all available methods
//
// List all available methods which can be used as condition for executions.
rpc ListAvailableExecutionMethods (ListAvailableExecutionMethodsRequest) returns (ListAvailableExecutionMethodsResponse) {
option (google.api.http) = {
get: "/executions/methods"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "execution.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "List all methods successfully";
};
};
};
}
// List all available service
//
// List all available services which can be used as condition for executions.
rpc ListAvailableExecutionServices (ListAvailableExecutionServicesRequest) returns (ListAvailableExecutionServicesResponse) {
option (google.api.http) = {
get: "/executions/services"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "execution.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "List all services successfully";
};
};
};
}
}

message CreateTargetRequest {
Target target = 2;
}

message CreateTargetResponse {
zitadel.resources.object.v3alpha.Details details = 2;
}

message PatchTargetRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
PatchTarget target = 2;
}

message PatchTargetResponse {
zitadel.resources.object.v3alpha.Details details = 1;
}

message DeleteTargetRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
}

message DeleteTargetResponse {
zitadel.resources.object.v3alpha.Details details = 1;
}

message SearchTargetsRequest {
// list limitations and ordering.
zitadel.resources.object.v3alpha.ListQuery query = 2;
// the field the result is sorted.
zitadel.resources.action.v3alpha.TargetFieldName sorting_column = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"FIELD_NAME_SCHEMA_TYPE\""
}
];
// Define the criteria to query for.
repeated zitadel.resources.action.v3alpha.TargetSearchFilter filters = 4;
}

message SearchTargetsResponse {
zitadel.resources.object.v3alpha.ListDetails details = 1;
zitadel.resources.action.v3alpha.TargetFieldName sorting_column = 2;
repeated zitadel.resources.action.v3alpha.GetTarget result = 3;
}

message GetTargetRequest {
// unique identifier of the target.
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
}

message GetTargetResponse {
zitadel.resources.action.v3alpha.GetTarget target = 1;
}

message PutExecutionRequest {
Execution execution = 2;
}

message PutExecutionResponse {
zitadel.resources.object.v3alpha.Details details = 2;
}

message DeleteExecutionRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
}

message DeleteExecutionResponse {
zitadel.resources.object.v3alpha.Details details = 1;
}

message SearchExecutionsRequest {
// list limitations and ordering.
zitadel.resources.object.v3alpha.ListQuery query = 1;
// Define the criteria to query for.
repeated zitadel.resources.action.v3alpha.ExecutionSearchFilter filters = 2;
}

message SearchExecutionsResponse {
zitadel.resources.object.v3alpha.ListDetails details = 1;
repeated zitadel.resources.action.v3alpha.GetExecution result = 2;
}

message ListAvailableExecutionFunctionsRequest{}
message ListAvailableExecutionFunctionsResponse{
// All available functions
repeated string functions = 1;
}
message ListAvailableExecutionMethodsRequest{}
message ListAvailableExecutionMethodsResponse{
// All available methods
repeated string methods = 1;
}

message ListAvailableExecutionServicesRequest{}
message ListAvailableExecutionServicesResponse{
// All available services
repeated string services = 1;
}
action_target.proto
syntax = "proto3";

package zitadel.resources.action.v3alpha;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
import "zitadel/protoc_gen_zitadel/v2/options.proto";

import "zitadel/resources/object/v3alpha/object.proto";

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha;action";

message Target {
string name = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ip_allow_list\"";
}
];
// Defines the target type and how the response of the target is treated.
oneof target_type {
SetRESTWebhook rest_webhook = 4;
SetRESTRequestResponse rest_request_response = 5;
}
// Timeout defines the duration until ZITADEL cancels the execution.
google.protobuf.Duration timeout = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"10s\"";
}
];
oneof execution_type {
// Set the execution to run asynchronously.
bool is_async = 7;
// Define if any error stops the whole execution. By default the process continues as normal.
bool interrupt_on_error = 8;
}
}

message GetTarget {
zitadel.resources.object.v3alpha.Details details = 1;
Target target = 2;
}

message PatchTarget {
optional string name = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ip_allow_list\"";
}
];
// Defines the target type and how the response of the target is treated.
oneof target_type {
SetRESTWebhook rest_webhook = 3;
SetRESTRequestResponse rest_request_response = 4;
}
// Timeout defines the duration until ZITADEL cancels the execution.
optional google.protobuf.Duration timeout = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"10s\"";
}
];
oneof execution_type {
// Set the execution to run asynchronously.
bool is_async = 6;
// Define if any error stops the whole execution. By default the process continues as normal.
bool interrupt_on_error = 7;
}
}

message SetRESTWebhook {
string url = 1 [
(validate.rules).string = {min_len: 1, max_len: 1000, uri: true},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"https://example.com/hooks/ip_check\"";
}
];
}

message SetRESTRequestResponse {
string url = 1 [
(validate.rules).string = {min_len: 1, max_len: 1000, uri: true},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"https://example.com/hooks/ip_check\"";
}
];
}
action_execution.proto
syntax = "proto3";

package zitadel.resources.action.v3alpha;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
import "zitadel/protoc_gen_zitadel/v2/options.proto";

import "zitadel/resources/object/v3alpha/object.proto";

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha;action";

message Execution {
Condition condition = 1;
// Target IDs which are called when the defined conditions trigger.
repeated string targets = 2;
// Included executions with the same condition-types.
repeated string includes = 3;
}

message GetExecution {
zitadel.resources.object.v3alpha.Details details = 1;
Execution execution = 2;
}

message Condition {
// Condition-types under which conditions the execution should trigger. Only one is possible.
oneof condition_type {
option (validate.required) = true;

// Condition-type to execute after a request on the defined API point is received.
RequestExecution request = 1;
// Condition-type to execute before a response on the defined API point is returned.
ResponseExecution response = 2;
// Condition-type to execute when a function is used, replaces actions v1.
string function = 3;
// Condition-type to execute after an event is created in the system.
EventExecution event = 4;
}
}

message RequestExecution {
// Condition for the request execution. Only one is possible.
oneof condition{
// GRPC-method as condition.
string method = 1 [
(validate.rules).string = {min_len: 1, max_len: 1000},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"/zitadel.session.v2.SessionService/ListSessions\"";
}
];
// GRPC-service as condition.
string service = 2 [
(validate.rules).string = {min_len: 1, max_len: 1000},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"zitadel.session.v2.SessionService\"";
}
];
// All calls to any available services and methods as condition.
bool all = 3;
}
}

message ResponseExecution {
// Condition for the response execution. Only one is possible.
oneof condition{
// GRPC-method as condition.
string method = 1 [
(validate.rules).string = {min_len: 1, max_len: 1000},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"/zitadel.session.v2.SessionService/ListSessions\"";
}
];
// GRPC-service as condition.
string service = 2 [
(validate.rules).string = {min_len: 1, max_len: 1000},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"zitadel.session.v2.SessionService\"";
}
];
// All calls to any available services and methods as condition.
bool all = 3;
}
}

message EventExecution{
// Condition for the event execution. Only one is possible.
oneof condition{
// Event name as condition.
string event = 1 [
(validate.rules).string = {min_len: 1, max_len: 1000},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"user.human.added\"";
}
];
// Event group as condition, all events under this group.
string group = 2 [
(validate.rules).string = {min_len: 1, max_len: 1000},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 1000,
example: "\"user.human\"";
}
];
// all events as condition.
bool all = 3;
}
}

action_query.proto
syntax = "proto3";

package zitadel.resources.action.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha;action";

import "google/api/field_behavior.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

import "zitadel/resources/object/v3alpha/object.proto";
import "zitadel/resources/action/v3alpha/execution.proto";

message ExecutionSearchFilter {
oneof filter {
option (validate.required) = true;

InConditionsFilter in_conditions = 1;
ExecutionTypeFilter execution_type = 2;
TargetFilter target = 3;
IncludeFilter include = 4;
}
}

message InConditionsFilter {
// Defines the conditions to query for.
repeated Condition conditions = 1;
}

message ExecutionTypeFilter {
// Defines the type to query for.
ExecutionType type = 1;
}

message TargetFilter {
// Defines the id to query for.
string id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "the id of the targets to include"
example: "\"69629023906488334\"";
}
];
}

message IncludeFilter {
// Defines the include to query for.
string include = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "the id of the include"
example: "\"request.zitadel.session.v2.SessionService\"";
}
];
}

message TargetSearchFilter {
oneof query {
option (validate.required) = true;

TargetNameFilter name = 1;
InTargetIDsFilter in_ids = 2;
}
}

message TargetNameFilter {
// Defines the name of the target to query for.
string name = 1 [
(validate.rules).string = {max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
max_length: 200;
example: "\"ip_allow_list\"";
}
];
// Defines which text comparison method used for the name query.
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
(validate.rules).enum.defined_only = true,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines which text equality method is used";
}
];
}

message InTargetIDsFilter {
// Defines the ids to query for.
repeated string ids = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "the ids of the targets to include"
example: "[\"69629023906488334\",\"69622366012355662\"]";
}
];
}

enum ExecutionType {
EXECUTION_TYPE_UNSPECIFIED = 0;
EXECUTION_TYPE_REQUEST = 1;
EXECUTION_TYPE_RESPONSE = 2;
EXECUTION_TYPE_EVENT = 3;
EXECUTION_TYPE_FUNCTION = 4;
}

enum TargetFieldName {
TARGET_FIELD_NAME_UNSPECIFIED = 0;
TARGET_FIELD_NAME_ID = 1;
TARGET_FIELD_NAME_CREATION_DATE = 2;
TARGET_FIELD_NAME_CHANGE_DATE = 3;
TARGET_FIELD_NAME_NAME = 4;
TARGET_FIELD_NAME_TARGET_TYPE = 5;
TARGET_FIELD_NAME_URL = 6;
TARGET_FIELD_NAME_TIMEOUT = 7;
TARGET_FIELD_NAME_ASYNC = 8;
TARGET_FIELD_NAME_INTERRUPT_ON_ERROR = 9;
}

ZITADELUsers

Standard CRUD methods

ZITADELUserSchemas

Standard CRUD methods

ZITADELIdentityProviders

  • Standard CRUD and methods for all IDPs
  • Resources have additional properties for reusability capabilities.
idp_service.proto
syntax = "proto3";

package zitadel.resources.idp.v3alpha;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
import "zitadel/protoc_gen_zitadel/v2/options.proto";

import "zitadel/resources/object/v3alpha/object.proto";
import "zitadel/resources/idp/v3alpha/search.proto";
import "zitadel/resources/idp/v3alpha/idp.proto";
import "zitadel/resources/idp/v3alpha/gitlab.proto";
import "zitadel/object/v3alpha/object.proto";

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/idp/v3alpha;idp";

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Identity Provider Service";
version: "3.0-alpha";
description: "This API is intended to manage identity providers (IDPs). IDPs can be created for specific organizations or for an instance. IDPs created on an instance can be activated (reused) or deactivated in organizations. It is behind the feature flag \"multitenancy_resources_api\". It will continue breaking as long as it is in alpha state.";
contact:{
name: "ZITADEL"
url: "https://zitadel.com"
email: "hi@zitadel.com"
}
license: {
name: "Apache 2.0",
url: "https://github.com/zitadel/zitadel/blob/main/LICENSE";
};
};
schemes: HTTPS;
schemes: HTTP;

consumes: "application/json";
consumes: "application/grpc";

produces: "application/json";
produces: "application/grpc";

consumes: "application/grpc-web+proto";
produces: "application/grpc-web+proto";

host: "$ZITADEL_DOMAIN";
base_path: "/resources/v3alpha";

external_docs: {
description: "Detailed information about ZITADEL",
url: "https://zitadel.com/docs"
}
security_definitions: {
security: {
key: "OAuth2";
value: {
type: TYPE_OAUTH2;
flow: FLOW_ACCESS_CODE;
authorization_url: "$CUSTOM-DOMAIN/oauth/v2/authorize";
token_url: "$CUSTOM-DOMAIN/oauth/v2/token";
scopes: {
scope: {
key: "openid";
value: "openid";
}
scope: {
key: "urn:zitadel:iam:org:project:id:zitadel:aud";
value: "urn:zitadel:iam:org:project:id:zitadel:aud";
}
}
}
}
}
security: {
security_requirement: {
key: "OAuth2";
value: {
scope: "openid";
scope: "urn:zitadel:iam:org:project:id:zitadel:aud";
}
}
}
responses: {
key: "403";
value: {
description: "Returned when the user does not have permission to access the resource.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
responses: {
key: "404";
value: {
description: "Returned when the resource does not exist.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
};

service ZITADELIdentityProviders {

// Create a GitLab IDP
rpc CreateGitLabIDP (CreateGitLabIDPRequest) returns (CreateGitLabIDPResponse) {
option (google.api.http) = {
post: "/idps/gitlab"
body: "idp"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "idp.write"
}
http_response: {
success_code: 201
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "201";
value: {
description: "GitLabIDP successfully created";
schema: {
json_schema: {
ref: "#/definitions/v2CreateGitLabIDPResponse";
}
}
};
};
};
}

// Patch a GitLab IDP
rpc PatchGitLabIDP (PatchGitLabIDPRequest) returns (PatchGitLabIDPResponse) {
option (google.api.http) = {
patch: "/idps/gitlab/{id}"
body: "idp"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "idp.write"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "GitLabIDP successfully updated";
};
};
};
}

// Find a GitLab IDP by ID
rpc GetGitLabIDP (GetGitLabIDPRequest) returns (GetGitLabIDPResponse) {
option (google.api.http) = {
get: "/idps/gitlab/{id}"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "idp.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200"
value: {
description: "GitLabIDP successfully retrieved";
}
};
};
}

// Delete an IDP of any type
rpc DeleteIDP (DeleteIDPRequest) returns (DeleteIDPResponse) {
option (google.api.http) = {
delete: "/idps/{id}"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "idp.delete"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "Identity provider successfully deleted";
};
};
};
}

// Search IDPs
//
// Search all matching IDPs. By default, all instance-level and organization-level providers of all types are returned.
// Only type-agnostic properties are returned in the response.
// To get the full details of a specific IDP, use the specific types Get method.
// If you search by passing an organization context, the state and the state policy might be different than if you search within the default instance-level context.
// Make sure to include a limit and sorting for pagination.
rpc SearchIDPs (SearchIDPsRequest) returns (SearchIDPsResponse) {
option (google.api.http) = {
post: "/idps/_search",
body: "filters"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "idp.read"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "A list of all IDPs matching the query";
};
};
responses: {
key: "400";
value: {
description: "invalid list query";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
};
};
};
};
};
}
}

message CreateGitLabIDPRequest {
optional zitadel.object.v3alpha.RequestContext ctx = 1;
GitLabIDP idp = 2;
}

message CreateGitLabIDPResponse {
zitadel.resources.object.v3alpha.Details details = 2;
}

message PatchGitLabIDPRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
PatchGitLabIDP idp = 2;
}

message PatchGitLabIDPResponse {
zitadel.resources.object.v3alpha.Details details = 1;
}

message DeleteIDPRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
}

message DeleteIDPResponse {
zitadel.resources.object.v3alpha.Details details = 1;
}

message SearchIDPsRequest {
optional zitadel.object.v3alpha.RequestContext ctx = 1;
// list limitations and ordering.
zitadel.resources.object.v3alpha.ListQuery query = 2;
// the field the result is sorted.
zitadel.resources.idp.v3alpha.IDPFieldName sorting_column = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"FIELD_NAME_SCHEMA_TYPE\""
}
];
repeated zitadel.resources.idp.v3alpha.IDPSearchFilter filters = 4;
}

message SearchIDPsResponse {
zitadel.resources.object.v3alpha.ListDetails details = 1;
zitadel.resources.idp.v3alpha.IDPFieldName sorting_column = 2;
repeated zitadel.resources.idp.v3alpha.GetIDP result = 3;
}

message GetGitLabIDPRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1,
max_length: 200,
example: "\"69629026806489455\"";
}
];
}

message GetGitLabIDPResponse {
zitadel.resources.idp.v3alpha.GetGitLabIDP idp = 1;
}
idp.proto
syntax = "proto3";

package zitadel.resources.idp.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/idp/v3alpha;idp";

import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

import "zitadel/resources/object/v3alpha/object.proto";

message IDP {
string name = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"GitLab\"";
}
];
zitadel.resources.object.v3alpha.StatePolicy state_policy = 2;
Options options = 3;
}

message PatchIDP {
optional string name = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"GitLab\"";
}
];
optional zitadel.resources.object.v3alpha.StatePolicy state_policy = 2;
optional Options options = 3;
}


message GetIDP {
zitadel.resources.object.v3alpha.Details details = 1;
optional zitadel.resources.object.v3alpha.Parent parent = 2;
zitadel.resources.object.v3alpha.State state = 3;
ProviderType type = 4;
IDP idp = 5;
}

enum ProviderType {
PROVIDER_TYPE_UNSPECIFIED = 0;
PROVIDER_TYPE_OIDC = 1;
PROVIDER_TYPE_JWT = 2;
PROVIDER_TYPE_LDAP = 3;
PROVIDER_TYPE_OAUTH = 4;
PROVIDER_TYPE_AZURE_AD = 5;
PROVIDER_TYPE_GITHUB = 6;
PROVIDER_TYPE_GITHUB_ES = 7;
PROVIDER_TYPE_GITLAB = 8;
PROVIDER_TYPE_GITLAB_SELF_HOSTED = 9;
PROVIDER_TYPE_GOOGLE = 10;
PROVIDER_TYPE_APPLE = 11;
PROVIDER_TYPE_SAML = 12;
}


enum AutoLinkingOption {
// AUTO_LINKING_OPTION_UNSPECIFIED disables the auto linking prompt.
AUTO_LINKING_OPTION_UNSPECIFIED = 0;
// AUTO_LINKING_OPTION_USERNAME will use the username of the external user to check for a corresponding ZITADEL user.
AUTO_LINKING_OPTION_USERNAME = 1;
// AUTO_LINKING_OPTION_EMAIL will use the email of the external user to check for a corresponding ZITADEL user with the same verified email
// Note that in case multiple users match, no prompt will be shown.
AUTO_LINKING_OPTION_EMAIL = 2;
}

message Options {
bool is_linking_allowed = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Enable if users should be able to link an existing ZITADEL user with an external account.";
}
];
bool is_creation_allowed = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Enable if users should be able to create a new account in ZITADEL when using an external account.";
}
];
bool is_auto_creation = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Enable if a new account in ZITADEL should be created automatically when login with an external account.";
}
];
bool is_auto_update = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Enable if a the ZITADEL account fields should be updated automatically on each login.";
}
];
AutoLinkingOption auto_linking = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Enable if users should get prompted to link an existing ZITADEL user to an external account if the selected attribute matches.";
}
];
}
idp_search.proto
syntax = "proto3";

package zitadel.resources.idp.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/idp/v3alpha;idp";

import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

import "zitadel/resources/object/v3alpha/object.proto";

enum IDPFieldName {
IDP_FIELD_NAME_UNSPECIFIED = 0;
IDP_FIELD_NAME_NAME = 1;
}

message IDPSearchFilter {
oneof filter {
IDPIDFilter id = 1;
IDPNameFilter name = 2;
resources.object.v3alpha.StateFilter state = 3;
}
}

message IDPIDFilter {
string id = 1 [
(validate.rules).string = {max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
}
];
}

message IDPNameFilter {
string name = 1 [
(validate.rules).string = {max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"google\"";
}
];
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
(validate.rules).enum.defined_only = true,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines which text equality method is used";
}
];
}
idp_gitlab.proto
syntax = "proto3";

package zitadel.resources.idp.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/idp/v3alpha;idp";

import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

import "zitadel/resources/object/v3alpha/object.proto";
import "zitadel/resources/idp/v3alpha/idp.proto";

message GetGitLabIDP {
zitadel.resources.object.v3alpha.Details details = 1;
optional zitadel.resources.object.v3alpha.Parent parent = 2;
zitadel.resources.object.v3alpha.State state = 3;
ProviderType type = 4;
GitLabIDP idp = 5;
}

message GitLabIDP {
IDP idp = 1;
GitLabConfig config = 2;
}

message PatchGitLabIDP {
optional PatchIDP idp = 1;
optional PatchGitLabConfig config = 2;
}

message GitLabConfig {
string client_id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"client-id\"";
description: "client id of the GitLab application";
}
];
repeated string scopes = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[\"openid\", \"profile\", \"email\"]";
description: "the scopes requested by ZITADEL during the request to GitLab";
}
];
}

message PatchGitLabConfig {
optional string client_id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"client-id\"";
description: "client id of the GitLab application";
}
];
repeated string scopes = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[\"openid\", \"profile\", \"email\"]";
description: "the scopes requested by ZITADEL during the request to GitLab";
}
];
}
object.proto
syntax = "proto3";

package zitadel.object.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha;object";

import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

message RequestContext {
// By default, the request context is set to the instance discovered by the domain from the requests host header.
oneof owner {
bool system = 1 [(validate.rules).bool = {const: true}]; // TODO: move the source of truth from the defaults.yaml into the database
string instance_id = 2;
string instance_domain = 3;
string org_id = 4;
string org_domain = 5;
}
}

enum OwnerType {
OWNER_TYPE_UNSPECIFIED = 0;
OWNER_TYPE_SYSTEM = 1; // TODO: move the source of truth from the defaults.yaml into the database
OWNER_TYPE_INSTANCE = 2;
OWNER_TYPE_ORG = 3;
}

message Owner {
OwnerType type = 1;
string id = 2;
}

resource_object.proto
syntax = "proto3";

package zitadel.resources.object.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha;object";

import "google/api/field_behavior.proto";
import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

import "zitadel/object/v3alpha/object.proto";

message Organization {
oneof org {
string org_id = 1;
string org_domain = 2;
}
}

message Details {
string id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629012906488334\"";
}
];

//sequence represents the order of events. It's always counting
//
// on read: the sequence of the last event reduced by the projection
//
// on manipulation: the timestamp of the event(s) added by the manipulation
uint64 sequence = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2\"";
}
];
//change_date is the timestamp when the object was changed
//
// on read: the timestamp of the last event reduced by the projection
//
// on manipulation: the timestamp of the event(s) added by the manipulation
google.protobuf.Timestamp change_date = 3;
//resource_owner represents the context an object belongs to
zitadel.object.v3alpha.Owner resource_owner = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
}
];
}

enum State {
EFFECTIVE_STATE_UNSPECIFIED = 0;
EFFECTIVE_STATE_ACTIVE = 1;
EFFECTIVE_STATE_INACTIVE = 2;
}

enum StatePolicy {
STATE_POLICY_UNSPECIFIED = 0;
STATE_POLICY_ACTIVATE = 1;
STATE_POLICY_DEACTIVATE = 2;
STATE_POLICY_INHERIT = 3;
}

message StateFilter {
// Defines the state to query for.
resources.object.v3alpha.State state = 1 [
(validate.rules).enum.defined_only = true,
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"STATE_ACTIVE\""
}
];
}

message Parent {
zitadel.object.v3alpha.Owner parent = 1;
State state = 2;
}

message ListQuery {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "General List Query"
description: "Object unspecific list filters like offset, limit and asc/desc."
}
};
uint64 offset = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"0\"";
}
];
uint32 limit = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "100";
description: "Maximum amount of events returned. The default is 100, the maximum is 1000. If the limit exceeds the maximum, ZITADEL throws an error.";
}
];
bool asc = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "default is descending"
}
];
}

message ListDetails {
uint32 applied_limit = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "100";
}
];
bool end_of_list = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
}
];
uint64 total_result = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2\"";
}
];
uint64 processed_sequence = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"267831\"";
}
];
google.protobuf.Timestamp timestamp = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "the last time the projection got updated"
}
];
}

enum TextFilterMethod {
TEXT_FILTER_METHOD_EQUALS = 0;
TEXT_FILTER_METHOD_EQUALS_IGNORE_CASE = 1;
TEXT_FILTER_METHOD_STARTS_WITH = 2;
TEXT_FILTER_METHOD_STARTS_WITH_IGNORE_CASE = 3;
TEXT_FILTER_METHOD_CONTAINS = 4;
TEXT_FILTER_METHOD_CONTAINS_IGNORE_CASE = 5;
TEXT_FILTER_METHOD_ENDS_WITH = 6;
TEXT_FILTER_METHOD_ENDS_WITH_IGNORE_CASE = 7;
}

enum ListFilterMethod {
LIST_FILTER_METHOD_IN = 0;
}

enum TimestampFilterMethod {
TIMESTAMP_Filter_METHOD_EQUALS = 0;
TIMESTAMP_Filter_METHOD_GREATER = 1;
TIMESTAMP_Filter_METHOD_GREATER_OR_EQUALS = 2;
TIMESTAMP_Filter_METHOD_LESS = 3;
TIMESTAMP_Filter_METHOD_LESS_OR_EQUALS = 4;
}

ZITADELInstances

Additional to the standard CRUD methods:

  • Limit (partial update of block and audit log retention)
  • BulkLimit (partial update of block and audit log retention for multiple instances)

ZITADELOrganizations

Additional to the standard CRUD methods:

  • SetAsInstanceDefault
  • GetInstanceDefault

ZITADELDomains

Additional to the standard CRUD methods:

  • SetAsPrimary
  • Validate

ZITADELSessions

Standard CRUD methods

ZITADELProjects

Standard CRUD methods

ZITADELApps

Standard CRUD methods

ZITADELMemberships

The given context defines the organization, instance or system where the membership is created. The context and the user ID together are unique.

Additional to the standard CRUD methods:

  • ListAvailableRoles (context-aware)

ZITADELGrants

  • Standard CRUD methods for project grants
  • Standard CRUD methods for user grants
  • Standard CRUD methods for roles

ZITADELSMTPProviders

Standard CRUD methods

ZITADELSMSProviders

Standard CRUD methods

Settings

Settings have no identity (ID) and are always context-aware. They also don't have a state like active or inactive. They only have properties that can be set and queried. These properties are inherited to from parent-contexts (instance) to child-contexts (organization).

Settings behave like this:

  • Setting and retrieving settings is always context-aware. By default, the context is the instance discovered by the requests Host header.
  • All settings properties can be partially overridden in child-contexts.
  • All settings properties can be partially reset in child-contexts, so their values default to the parent contexts property values.
  • All settings properties returned by queries contain the value and if it is inherited, the context where it is inherited from.

For a full proto example, have a look at the ZITADELLanguageSettings service.

Settings Services

ZITADELLanguageSettings

Default language, restricted languages, supported languages

language_service.proto
syntax = "proto3";

package zitadel.settings.language.v3alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/language/v3alpha;language";

import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";

import "zitadel/object/v3alpha/object.proto";
import "zitadel/settings/object/v3alpha/object.proto";
import "zitadel/settings/language/v3alpha/language.proto";
import "zitadel/protoc_gen_zitadel/v2/options.proto";

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Language Settings Service";
version: "3.0-alpha";
description: "Language Service is intended to manage languages for ZITADEL. Enable the feature flag \"multitenancy_settings\" in order to activate it. Languages are settings, and are therefore inherited through the context hierarchy system -> instance -> org. It will continue breaking as long as it is in alpha state.";
contact:{
name: "ZITADEL"
url: "https://zitadel.com"
email: "hi@zitadel.com"
}
license: {
name: "Apache 2.0",
url: "https://github.com/zitadel/zitadel/blob/main/LICENSE";
};
};
schemes: HTTPS;
schemes: HTTP;

consumes: "application/json";
consumes: "application/grpc";
consumes: "application/grpc-web+proto";

produces: "application/json";
produces: "application/grpc";
produces: "application/grpc-web+proto";

host: "$ZITADEL_DOMAIN";
base_path: "/settings/v3alpha";

external_docs: {
description: "Detailed information about ZITADEL",
url: "https://zitadel.com/docs"
}
security_definitions: {
security: {
key: "OAuth2";
value: {
type: TYPE_OAUTH2;
flow: FLOW_ACCESS_CODE;
authorization_url: "$ZITADEL_DOMAIN/oauth/v2/authorize";
token_url: "$ZITADEL_DOMAIN/oauth/v2/token";
scopes: {
scope: {
key: "openid";
value: "openid";
}
scope: {
key: "urn:zitadel:iam:org:project:id:zitadel:aud";
value: "urn:zitadel:iam:org:project:id:zitadel:aud";
}
}
}
}
}
security: {
security_requirement: {
key: "OAuth2";
value: {
scope: "openid";
scope: "urn:zitadel:iam:org:project:id:zitadel:aud";
}
}
}
responses: {
key: "403";
value: {
description: "Returned when the user does not have permission to access the settings in the given context.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
};

// ZITADELLanguageSettings is intended to manage languages for ZITADEL.
// Enable the feature flag \"multitenancy_settings\" in order to activate it.
// Languages are settings, and are therefore inherited through the context hierarchy system -> instance -> org.
service ZITADELLanguageSettings {
rpc SetLanguages (SetLanguageSettingsRequest) returns (SetLanguageSettingsResponse) {
option (google.api.http) = {
patch: "/languages"
body: "settings"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Set languages for a given context";
description: "Configure and set languages for a given context. Only fields present in the request are set or unset."
responses: {
key: "200"
value: {
description: "OK";
}
};
};
};

rpc ResolveLanguages (ResolveLanguageSettingsRequest) returns (ResolveLanguageSettingsResponse) {
option (google.api.http) = {
get: "/languages"
};

option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the languages in the given context";
description: "Returns all configured and inherited languages for the given context."
responses: {
key: "200"
value: {
description: "OK";
}
};
};
};
}

message SetLanguageSettingsRequest{
optional zitadel.object.v3alpha.RequestContext ctx = 1;
SetLanguageSettings settings = 2;
}

message SetLanguageSettingsResponse{
zitadel.settings.object.v3alpha.Details details = 1;
}

message ResolveLanguageSettingsRequest{
optional zitadel.object.v3alpha.RequestContext ctx = 1;
}

message ResolveLanguageSettingsResponse{
zitadel.settings.object.v3alpha.Details details = 1;
ResolvedLanguageSettings settings = 2;
}
language.proto
syntax = "proto3";

package zitadel.settings.language.v3alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/language/v3alpha;language";

import "validate/validate.proto";
import "protoc-gen-openapiv2/options/annotations.proto";

import "zitadel/settings/object/v3alpha/object.proto";
import "zitadel/object/v3alpha/object.proto";

message SetLanguageSettings {
optional zitadel.settings.object.v3alpha.Language default_language = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "default language for the current context"
example: "\"en\""
}
];
optional SetLanguages restricted_languages = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "To these languages, message texts and default login UI labels are translated to. Also, the discovery endpoint only lists these languages."
example: "[\"en\", \"de\"]"
}
];
}

message ResolvedLanguageSettings {
zitadel.settings.object.v3alpha.ResolvedString default_language = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "default language for the current context"
example: "\"en\""
}
];
ResolvedLanguages restricted_languages = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "To these languages, message texts and default login UI labels are translated to. Also, the discovery endpoint only lists these languages."
example: "[\"en\", \"de\"]"
}
];
ResolvedLanguages supported_languages = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "These languages are supported by the system. For simplicity, the field is of type ResolvedLanguages, even though the list is immutable and the owner is always of OWNER_TYPE_SYSTEM."
example: "[\"en\", \"de\", \"it\"]"
}
];
}

message SetLanguages {
repeated zitadel.settings.object.v3alpha.Language languages = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "List of languages to set"
example: "[\"en\", \"de\"]"
}
];
}

message ResolvedLanguages {
repeated zitadel.settings.object.v3alpha.Language value = 1[
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "List of languages"
example: "[\"en\", \"de\"]"
}
];
optional zitadel.object.v3alpha.Owner owner = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If the value is inherited, the value is inherited from this owner.";
}
];
}
object.proto
syntax = "proto3";

package zitadel.object.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha;object";

import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";

message RequestContext {
// By default, the request context is set to the instance discovered by the domain from the requests host header.
oneof owner {
bool system = 1 [(validate.rules).bool = {const: true}]; // TODO: move the source of truth from the defaults.yaml into the database
string instance_id = 2;
string instance_domain = 3;
string org_id = 4;
string org_domain = 5;
}
}

enum OwnerType {
OWNER_TYPE_UNSPECIFIED = 0;
OWNER_TYPE_SYSTEM = 1; // TODO: move the source of truth from the defaults.yaml into the database
OWNER_TYPE_INSTANCE = 2;
OWNER_TYPE_ORG = 3;
}

message Owner {
OwnerType type = 1;
string id = 2;
}

settings_object.proto
syntax = "proto3";

package zitadel.settings.object.v3alpha;

option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/object/v3alpha;object";

import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
import "google/protobuf/duration.proto";

import "zitadel/object/v3alpha/object.proto";

message Details {
//sequence represents the order of events. It's always counting
//
// on read: the sequence of the last event reduced by the projection
//
// on manipulation: the timestamp of the event(s) added by the manipulation
uint64 sequence = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2\"";
}
];
//change_date is the timestamp when the object was changed
//
// on read: the timestamp of the last event reduced by the projection
//
// on manipulation: the timestamp of the event(s) added by the manipulation
google.protobuf.Timestamp change_date = 2;
//resource_owner represents the context an object belongs to
zitadel.object.v3alpha.Owner owner = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
}
];
}


message ResolvedBool {
bool value = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "false";
description: "The resolved value is valid for the given context. Either the value was explicitly set for the given context or it is inherited from a higher-level context.";
}
];
optional zitadel.object.v3alpha.Owner owner = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If the value is inherited, the value is inherited from this owner.";
}
];
}

message SetBool {
oneof value {
bool set = 1;
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}

message ResolvedString {
string value = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"a resolved string\"";
description: "The resolved value is valid for the given context. Either the value was explicitly set for the given context or it is inherited from a higher-level context.";
}
];
optional zitadel.object.v3alpha.Owner owner = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If the value is inherited, the value is inherited from this owner.";
}
];
}

message SetString {
oneof value {
string set = 1 [
(validate.rules).string = {
max_len: 256
}
];
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}

message SetStringLong {
oneof value {
string set = 1 [
(validate.rules).string = {
max_len: 2048
}
];
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}


message SetStringShort {
oneof value {
string set = 1 [
(validate.rules).string = {
max_len: 64
}
];
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}

message Language {
string key = 1 [(validate.rules).string = {pattern: "^[a-z]{2}$"}];
}

message ResolvedStrings {
repeated string value = 1[
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[\"a\", \"resolved\", \"list\", \"of\", \"strings\"]";
description: "The resolved value is valid for the given context. Either the value was explicitly set for the given context or it is inherited from a higher-level context.";
}
];
optional zitadel.object.v3alpha.Owner owner = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If the value is inherited, the value is inherited from this owner.";
}
];
}

message SetStrings {
oneof value {
SetStringsValue set = 1;
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}

message SetStringsValue {
repeated string value = 1;
}

message ResolvedUInt64 {
uint64 value = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "1000";
description: "The resolved value is valid for the given context. Either the value was explicitly set for the given context or it is inherited from a higher-level context.";
}
];
optional zitadel.object.v3alpha.Owner owner = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If the value is inherited, the value is inherited from this owner.";
}
];
}

message SetUInt64 {
oneof value {
uint64 set = 1;
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}


message ResolvedDuration {
google.protobuf.Duration value = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"5s\"";
description: "The resolved value is valid for the given context. Either the value was explicitly set for the given context or it is inherited from a higher-level context.";
}
];
optional zitadel.object.v3alpha.Owner owner = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If the value is inherited, the value is inherited from this owner.";
}
];
}

message SetDuration {
oneof value {
google.protobuf.Duration set = 1;
bool reset = 2 [(validate.rules).bool = {
const: true
}];
}
}

ZITADELTextSettings

Key-value pairs for localized login texts, previously known as login texts

ZITADELBrandingSettings

Predefined branding settings and custom key-value pairs, previously known as label policy or branding settings

ZITADELLoginSettings

Previously known as login policy

ZITADELLockoutSettings

Previously known as lockout policy

ZITADELPasswordSettings

Previously known as password complexity policy

ZITADELHelpSettings

Previously known as legal and support settings or privacy policy

ZITADELDomainSettings

Previously known as domain policy

ZITADELFeatureSettings

Feature toggles

Also contains disallow public org registrations on system and instance level.

ZITADELTemplatesSettings

HTML and text templates for fully customizable emails and sms

ZITADELSecretSettings

Replaces secret generators

API Release Policy

  • Defined but not yet implemented APIs are subject to change without further notice.
  • Once an API definition is implemented, it is released as Preview and is available for testing.
  • When a Preview API is tested enough so the concepts are proven to work, a new Beta API is released.
  • When an API is feature-complete and stable enough, a new GA (General Availability) API is released.
  • In all stages, changes to already implemented APIs are done in a backwards-compatible way, if possible.
  • When we release a new stage for an API, we deprecate the previous stage and keep it available for a smooth transition.

Preview APIs

These APIs are ready for testing and feedback. Beware, they don't yet follow all the rules defined above.