Provision Storage for Each Tenant with the Partner API
  • 07 Oct 2026
  • Dark
    Light

Provision Storage for Each Tenant with the Partner API

  • Dark
    Light

Article summary

The Backblaze Partner API lets a platform provider create and manage a separate Backblaze B2 Cloud Storage account for each customer, or tenant. Your control plane can use the Partner API to create the tenant account, the Backblaze B2 Native API to create app keys, and the S3-Compatible API to create buckets and store objects.

This guide describes an example provisioning workflow for Powered By Backblaze partners, neoclouds, managed service providers, and other platforms that provide Backblaze B2 storage through a branded customer experience.

Choose a Tenant-Isolation Model

Backblaze supports several ways to separate tenant data. This guide recommends a separate Backblaze account for each tenant when you need strong administrative isolation, separate usage records, or a clean account-transfer path.

Model

How it works

Use it when

Considerations

Account per tenant

The Partner API creates a separate managed Backblaze B2 account for each tenant.

You need strong tenant isolation, account-level usage reporting, or the option to transfer an account to the customer.

This is the primary model in this guide. Each managed group supports up to 5,000 members.

Bucket per tenant

Your platform creates one bucket for each tenant in a shared Backblaze B2 account.

You want fewer accounts and can manage tenant isolation with bucket-restricted app keys.

The Partner API does not create the tenant or bucket. A B2 account supports up to 100 buckets by default. Shared account administration and reporting remain the platform's responsibility.

Prefix per tenant

Your platform stores each tenant's objects under a unique prefix in a shared bucket.

You have many small tenants and your application already enforces tenant boundaries.

This model has the largest shared administrative boundary. Use prefix-restricted app keys when tenants connect directly, and test every list, read, write, and delete path.

Do not create a separate managed group for every tenant unless your commercial or administrative model requires it. A managed group is normally the container for many tenant accounts.

Choose a Tenant-Access Model

After provisioning the account and bucket, decide who holds the tenant workload credentials.

Access model

How it works

Advantages

Responsibilities

Direct access

Your platform provides the tenant with a restricted app key, bucket name, and S3 endpoint. The tenant's application sends S3 requests directly to Backblaze B2.

Removes your platform from the object data path and lets tenants use common S3 tools and SDKs.

Securely deliver and rotate credentials. Limit each key to the required bucket, prefix, and capabilities.

Proxied access

Your platform retains the Backblaze B2 app key. The tenant authenticates to your service, and your service signs and sends S3 requests to Backblaze B2.

Gives your platform direct control over branding, authorization, quotas, and request policy.

Protect credentials and operate the proxy for the required availability, performance, logging, and security levels.

Both models are possible. You can also use a hybrid model, such as proxied control operations with direct, time-limited data access. Document the chosen trust boundary before you issue credentials.

Provisioning Workflow

The Partner API, Native API, and S3-Compatible API perform different parts of the workflow:

Stage

Interface

Result

Create the tenant account

Partner API

A new Backblaze account is added to your managed group in the selected region.

Bootstrap the tenant account

Native API

The platform authorizes the tenant account and creates a standard provisioning app key.

Create the private bucket

S3-Compatible API

The AWS CLI or an S3 SDK creates a private bucket for the tenant.

Create restricted credentials

Native API

The platform creates an app key restricted to the tenant bucket and required operations.

Store and retrieve objects

S3-Compatible API

The tenant or platform uses familiar S3 operations against the tenant endpoint.

Measure usage

Usage Reports

Daily reports provide storage and transaction usage for group members.

The Partner API creates and manages accounts and group membership. It does not create buckets, prefixes, or bucket policies. The S3-Compatible API cannot create app keys, so the bootstrap workflow must use the Native API for key management.

Prerequisites

Before you begin, ensure that you have the following items:

A managed group supports up to 5,000 group members. A Backblaze B2 account supports up to 100 buckets by default. Contact your Backblaze account representative if your provisioning model requires a different group or bucket structure.

Authorize the Partner Account

Use the group administrator's master app key ID and app key to call b2_authorize_account. The response includes an authorization token and the groupsApiUrl that you use for Partner API operations.

export PARTNER_APPLICATION_KEY_ID="<partner-master-application-key-id>"
export PARTNER_APPLICATION_KEY="<partner-master-application-key>"

PARTNER_AUTH_RESPONSE=$(curl --silent --show-error \
  --user "$PARTNER_APPLICATION_KEY_ID:$PARTNER_APPLICATION_KEY" \
  https://api.backblazeb2.com/b2api/v4/b2_authorize_account)

export PARTNER_AUTH_TOKEN=$(printf '%s' "$PARTNER_AUTH_RESPONSE" | jq -r '.authorizationToken')
export GROUPS_API_URL=$(printf '%s' "$PARTNER_AUTH_RESPONSE" | jq -r '.apiInfo.groupsApi.groupsApiUrl')
export ADMIN_ACCOUNT_ID=$(printf '%s' "$PARTNER_AUTH_RESPONSE" | jq -r '.accountId')

Do not expose the group administrator's master app key to tenants or include it in client-side code.

Create the Tenant Account

Before you call the Partner API, check your provisioning-state table for an existing Backblaze account ID for the tenant. This prevents a client retry from creating a duplicate account.

Call b2_create_group_member to create a Backblaze account and add it to your managed group:

export GROUP_ID="<managed-group-id>"
export TENANT_EMAIL="tenant-12345@example.com"
export TENANT_REGION="us-west"

TENANT_RESPONSE=$(curl --silent --show-error \
  -H "Authorization: $PARTNER_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"adminAccountId\": \"$ADMIN_ACCOUNT_ID\",
    \"groupId\": \"$GROUP_ID\",
    \"memberEmail\": \"$TENANT_EMAIL\",
    \"region\": \"$TENANT_REGION\"
  }" \
  "$GROUPS_API_URL/b2api/v3/b2_create_group_member")

Supported region values are us-east, us-west, ca-east, and eu-central. If you omit the region or pass null, Backblaze uses the current default region for new accounts.

The tenant email address must have a valid email format and cannot already belong to a Backblaze account. If the group uses SSO, the email address must belong to the configured SSO domain. A group-member account can belong to only one group.

The response contains the following values:

  • applicationKeyId

  • applicationKey

  • groupMember.accountId

  • groupMember.groupId

  • groupMember.region

  • groupMember.s3Endpoint

The new group-member account cannot initially sign in to Backblaze. The group administrator manages it through the managed group.

If the request returns method_failure, Backblaze starts a background rollback. Verify whether the member and account still exist before retrying.

Record the Tenant Mapping

Associate the new Backblaze account with the tenant record in your platform before you create additional resources.

Platform value

Backblaze value

Internal tenant ID

groupMember.accountId

Partner group

groupMember.groupId

Storage region

groupMember.region

S3 endpoint

groupMember.s3Endpoint

Provisioning state

Current completed workflow step

Store the returned app key ID and app key in a secrets-management system. Do not store app keys in application logs, analytics events, support tickets, or tenant-visible metadata.

Authorize the Tenant Account

Use the credentials returned by b2_create_group_member to authorize the new tenant account through the Native API:

export TENANT_APPLICATION_KEY_ID=$(printf '%s' "$TENANT_RESPONSE" | jq -r '.applicationKeyId')
export TENANT_APPLICATION_KEY=$(printf '%s' "$TENANT_RESPONSE" | jq -r '.applicationKey')

TENANT_AUTH_RESPONSE=$(curl --silent --show-error \
  --user "$TENANT_APPLICATION_KEY_ID:$TENANT_APPLICATION_KEY" \
  https://api.backblazeb2.com/b2api/v4/b2_authorize_account)

export TENANT_AUTH_TOKEN=$(printf '%s' "$TENANT_AUTH_RESPONSE" | jq -r '.authorizationToken')
export TENANT_ACCOUNT_ID=$(printf '%s' "$TENANT_AUTH_RESPONSE" | jq -r '.accountId')
export TENANT_API_URL=$(printf '%s' "$TENANT_AUTH_RESPONSE" | jq -r '.apiInfo.storageApi.apiUrl')
export TENANT_S3_ENDPOINT=$(printf '%s' "$TENANT_AUTH_RESPONSE" | jq -r '.apiInfo.storageApi.s3ApiUrl')

Authorization tokens are temporary. Call b2_authorize_account again when a token expires.

Create a Provisioning App Key

The S3-Compatible API requires a standard app key. It does not support the automatically created master app key. Create a temporary, account-level key that can create the tenant bucket:

PROVISIONING_KEY_RESPONSE=$(curl --silent --show-error \
  -H "Authorization: $TENANT_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"accountId\": \"$TENANT_ACCOUNT_ID\",
    \"capabilities\": [
      \"listAllBucketNames\",
      \"listBuckets\",
      \"readBuckets\",
      \"writeBuckets\"
    ],
    \"keyName\": \"tenant-provisioning-key\"
  }" \
  "$TENANT_API_URL/b2api/v4/b2_create_key")

export PROVISIONING_KEY_ID=$(printf '%s' "$PROVISIONING_KEY_RESPONSE" | jq -r '.applicationKeyId')
export PROVISIONING_KEY=$(printf '%s' "$PROVISIONING_KEY_RESPONSE" | jq -r '.applicationKey')

Do not restrict this key to a bucket because the bucket does not exist yet and the writeBuckets capability is not allowed on bucket-restricted keys. Delete the provisioning key after setup, or retain it only in the platform control plane if you need it for approved bucket-management operations.

Create a Private Bucket with the S3-Compatible API

Use the temporary provisioning key as the AWS access key ID and secret access key. The endpoint returned by b2_authorize_account includes the https:// scheme.

export AWS_ACCESS_KEY_ID="$PROVISIONING_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$PROVISIONING_KEY"
export TENANT_BUCKET_NAME="<globally-unique-bucket-name>"

aws s3api create-bucket \
  --bucket "$TENANT_BUCKET_NAME" \
  --acl private \
  --endpoint-url "$TENANT_S3_ENDPOINT"

Bucket names must be globally unique. Do not include tenant email addresses, customer names, protected health information, or other sensitive information in bucket names.

Backblaze B2 uses Server-Side Encryption with Backblaze-Managed Keys and AES256 as the effective default for new object data. No additional encryption request is required for the default. Configure Object Lock, Lifecycle Rules, CORS rules, access logging, and other settings according to the service that your platform provides.

If the tenant requires Object Lock, enable it when you create the bucket. You cannot enable Object Lock on an existing bucket.

Look Up the Bucket ID

The Native API uses the bucket ID to restrict an app key. Look up the bucket by name after the S3-Compatible API creates it:

BUCKET_RESPONSE=$(curl --silent --show-error \
  -H "Authorization: $TENANT_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"accountId\": \"$TENANT_ACCOUNT_ID\",
    \"bucketName\": \"$TENANT_BUCKET_NAME\"
  }" \
  "$TENANT_API_URL/b2api/v4/b2_list_buckets")

export TENANT_BUCKET_ID=$(printf '%s' "$BUCKET_RESPONSE" | jq -r '.buckets[0].bucketId')

Verify that TENANT_BUCKET_ID is not empty before you continue.

Create a Restricted Workload App Key

Do not provide the tenant workload with the initial administrative credential or the account-level provisioning key. Create a standard app key limited to the tenant bucket and required operations:

TENANT_KEY_RESPONSE=$(curl --silent --show-error \
  -H "Authorization: $TENANT_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"accountId\": \"$TENANT_ACCOUNT_ID\",
    \"capabilities\": [
      \"listAllBucketNames\",
      \"listBuckets\",
      \"readBuckets\",
      \"listFiles\",
      \"readFiles\",
      \"writeFiles\",
      \"deleteFiles\"
    ],
    \"keyName\": \"tenant-storage-key\",
    \"bucketIds\": [\"$TENANT_BUCKET_ID\"]
  }" \
  "$TENANT_API_URL/b2api/v4/b2_create_key")

export TENANT_WORKLOAD_KEY_ID=$(printf '%s' "$TENANT_KEY_RESPONSE" | jq -r '.applicationKeyId')
export TENANT_WORKLOAD_KEY=$(printf '%s' "$TENANT_KEY_RESPONSE" | jq -r '.applicationKey')

Remove deleteFiles if the tenant workload must not delete objects. Add retention, legal hold, encryption, logging, or notification capabilities only when the workload must manage those settings.

The app key is returned only once. Save it securely before discarding the response.

Delete the Temporary Provisioning Key

After the bucket is configured, delete the temporary provisioning key if your control plane does not need it:

curl --silent --show-error \
  -H "Authorization: $TENANT_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"applicationKeyId\": \"$PROVISIONING_KEY_ID\"}" \
  "$TENANT_API_URL/b2api/v4/b2_delete_key"

unset PROVISIONING_KEY_ID PROVISIONING_KEY
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY

Test the Tenant Workload

Test the restricted key before you make the tenant active. The following commands upload and list an object through the S3-Compatible API:

export AWS_ACCESS_KEY_ID="$TENANT_WORKLOAD_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$TENANT_WORKLOAD_KEY"

aws s3api put-object \
  --bucket "$TENANT_BUCKET_NAME" \
  --key "provisioning-test.txt" \
  --body "./provisioning-test.txt" \
  --endpoint-url "$TENANT_S3_ENDPOINT"

aws s3api list-objects-v2 \
  --bucket "$TENANT_BUCKET_NAME" \
  --prefix "provisioning-test" \
  --endpoint-url "$TENANT_S3_ENDPOINT"

Also verify that the restricted key cannot list or access another tenant's bucket.

Return or Retain the Tenant Configuration

For direct access, securely provide the tenant with the following values:

  • S3 endpoint

  • Bucket name

  • App key ID, used as the S3 access key ID

  • App key, used as the S3 secret access key

  • Region, if required by the client or SDK

For proxied access, retain these values in your secrets-management system. Return only the endpoint and authorization details for your own service.

Never return the Partner API credential, the initial administrative credential, or an account-level provisioning key to the tenant.

Track Usage

Backblaze Usage Reports provide daily storage and transaction information for accounts, groups, and organizations. Use the tenant account ID and bucket ID to associate report rows with the corresponding tenant in your platform.

Account and Group Usage Reports must be enabled by the Backblaze Support team or your account representative. When enabled, the reports backfill the previous seven days and then provide daily data. Reports are delivered as CSV files to a dedicated reports bucket.

Use Usage Reports to support:

  • Tenant usage dashboards

  • Internal capacity reporting

  • Customer billing

  • Usage reconciliation

Do not use Bucket Access Logs as a substitute for billing data. Bucket Access Logs are intended for monitoring, troubleshooting, and general security analysis.

Offboard a Tenant

Define your data-retention and account-transfer process before you eject a tenant from the group.

Complete the following tasks as required by your service agreement:

  1. Stop new storage operations in your control plane.

  2. Revoke or delete app keys issued to tenant workloads.

  3. Export, retain, or delete tenant data according to the applicable policy.

  4. Resolve active Object Lock retention periods and Legal Holds.

  5. Reconcile final usage and billing records.

  6. Determine whether the customer will retain ownership of the Backblaze account.

  7. Call b2_eject_group_member only after completing the preceding tasks.

Ejecting a group member removes the account from the group. It does not delete the Backblaze account, delete its data, or invalidate existing app keys. After ejection, the former group member must reset the account password and accept the Backblaze Terms of Service before signing in.

An account that was ejected cannot be added back through the Partner API. A group administrator can invite it to a group through the Backblaze web console.

Make Provisioning Safe to Retry

Treat provisioning as a stateful workflow instead of one transaction.

  • Store the tenant-to-account mapping immediately after account creation.

  • Record each completed step, including account creation, bucket creation, app key creation, and validation.

  • Before retrying, check whether the expected account, bucket, or key already exists.

  • Handle 429 responses with a delay and retry policy.

  • Handle 503 responses with exponential backoff.

  • If b2_create_group_member returns method_failure, verify the rollback state before retrying.

  • Never log response bodies that contain app key material.

  • Send unresolved partial provisioning states to an operational queue for review.

Verify Tenant Isolation

Before making the workflow available to customers, verify that:

  • Each tenant receives a distinct Backblaze account.

  • Tenant credentials cannot access another tenant's buckets or objects.

  • Administrative credentials are stored separately from workload credentials.

  • Workload app keys include only the required capabilities.

  • Tenant identifiers are correctly associated with account IDs and bucket IDs.

  • Provisioning failures do not leave untracked accounts or credentials.

  • Retrying a request does not create duplicate tenant accounts.

  • Direct-access tenants can rotate credentials without exposing administrative keys.

  • A proxied-access design enforces authorization before every storage request.

  • The offboarding process does not rely on group ejection to delete data or revoke credentials.

Troubleshooting

Use the following information to resolve common issues that may occur when you provision and manage tenant storage.

Resolve an Existing Tenant Email

b2_create_group_member requires an email address that is not already associated with a Backblaze account. Generate or collect a different address that meets your tenant identity model.

Resolve Group-Member Creation Errors

Verify that the group is managed, administrative privileges are enabled, Backblaze B2 is a group product, and the group administrator account is approved for Partner API access.

Resolve a Rejected Region

Use us-east, us-west, ca-east, or eu-central. If you omit the region, Backblaze assigns the current default region for new accounts.

Resolve an Invalid S3 Access Key

Verify that you created a standard app key with b2_create_key. The S3-Compatible API does not support the automatically created master app key. Also verify that the endpoint belongs to the same region and account as the app key.

Resolve Unauthorized Bucket Creation

Verify that the provisioning app key includes writeBuckets and is not restricted to a bucket.

Resolve Unauthorized App Key Creation

Verify that the Native API authorization token includes writeKeys. The S3-Compatible API cannot create app keys.

Resolve Access After Ejection

Ejection does not invalidate the account's app keys. Revoke issued credentials and complete the supported account-transfer or closure process separately.

Help us improve this guide. If you find an error, notice outdated information, or have suggestions for improvement, email techpubs@backblaze.com.


Was this article helpful?