# Managing Credentials via API ## Introduction When you [create a service account][getting-credentials] through the noon Partner portal, you download a `.json` key file that your application uses to [authenticate API requests][authenticating-requests]. The **API User Service** lets you do the same thing — create and remove these credentials — programmatically, without logging into the portal. Each credential is an RSA key pair tied to a service account. The private key is used to sign JWT tokens, which are exchanged for session cookies to authenticate your API calls. Every service account is identified by a `user_code` — a unique identifier that you'll pass to the API User Service when creating or removing credentials. :::info Relationship to Existing Credentials If you've already downloaded a `.json` key file from the portal and are using it to call noon APIs — that is exactly the type of credential this service manages. The API User Service gives you a programmatic way to create additional keys for the same service account, or revoke existing ones. ::: ### Identifying the Target Service Account Every create/remove call needs to know which service account it's operating on. You can identify the account two ways: - **`user_code`** — the canonical identifier for the service account. Prefer this if you have it. - **`channel_identifier`** — the service account's username (the email-like identifier such as `my-integrator-prj12345-abc@p99999.idp.noon.partners`). Use this when you don't have the `user_code` handy. If both are provided, `user_code` takes precedence. If only `channel_identifier` is provided, the service resolves it to a `user_code` server-side before performing the operation. :::tip Where do I find these values? Both values live in the credential payload you already have: - From the portal flow — see the `.json` key file you downloaded in [Getting Your Credentials][getting-credentials]. - From the OAuth flow — see the `result` object returned by the token exchange step in [Getting Credentials via OAuth][oauth-credentials]. The `channel_identifier` field on that response is the same value you'd pass here. This is especially useful for integrators completing the OAuth flow: the token-exchange response gives you a `channel_identifier` but no `user_code`, so you can drive credential rotation from the value you already have. ::: ## When to Use The portal works well for one-off credential creation, but there are scenarios where programmatic access is essential: - **Credential rotation** — You want to regularly cycle out old keys and replace them with new ones, without manual intervention. For example, rotating keys every 30 days as part of your security policy. - **Managing credentials at scale** — If you're an integrator managing service accounts across multiple seller projects (especially those created via the [OAuth flow][oauth-credentials]), manually downloading and rotating keys for each one becomes impractical. - **Incident response** — If a key is compromised, you need to revoke it immediately. Calling the remove endpoint is faster than navigating the portal, and can be triggered automatically by your monitoring systems. ## Prerequisites - You must have a **noon Partner account** and an existing **service account** (see [Getting Your Credentials][getting-credentials]) - You must be authenticated — follow the [Authentication Guide][authenticating-requests] to set up API access - You must have appropriate permissions (project owner) to manage the target service account ## Available Operations ### Creating a Credential The [CreateCredential][apiuser-create-credential-api] operation generates a new RSA key pair for a service account and returns the credential details, including the private key. Identify the target service account using either `user_code` or `channel_identifier` (see [Identifying the Target Service Account](#identifying-the-target-service-account) above). A few important things to understand: - **The private key.** When you create a credential, the response includes the private key in PEM format. :::warning Store Your Private Key Securely The private key is **only returned once** at creation time — noon does not store it. If you lose it, you will need to create a new credential. Treat it like a password: store it in a secrets manager, never commit it to source control, and never log it. ::: - **IP whitelisting is optional but recommended.** You can restrict a credential to a set of known IP addresses (up to 10). Requests from other IPs will be rejected. This is useful for locking down production credentials to your server IPs. - **Expiry is optional.** You can set an expiry timestamp so the credential automatically becomes invalid after a certain date. If you don't set one, the credential remains valid indefinitely (or until you explicitly remove it). - **Each service account can have up to 5 active keys.** This allows you to rotate credentials without downtime — create the new key, update your application, then remove the old key. You can request to have the quota for keys increased if needed. ### Response A successful call returns a `credential` object. See the [API Reference][apiuser-create-credential-api] for full schema details. ### Removing Credentials The [RemoveCredentials][apiuser-remove-credentials-api] operation deactivates credentials for a service account. As with CreateCredential, you can identify the target account using either `user_code` or `channel_identifier` (see [Identifying the Target Service Account](#identifying-the-target-service-account) above). You can either: - **Remove a specific credential** by providing its `key_id` (returned when the credential was created). This is useful for targeted revocation — for example, rotating out a single key while keeping others active. - **Remove all active credentials** by omitting the `key_id`. This is a nuclear option for when you need to immediately lock down a service account — for example, if you suspect the account is compromised. Removal is immediate. Any subsequent authentication attempts using a removed credential will fail. If there are no active credentials to remove, the operation still succeeds (with a count of zero). :::tip If there are no active credentials to remove, the API will return `success: true` with `credentials_removed: 0`. ::: ## Credential Rotation Example :::tip Stay within the key limit Each service account supports a maximum of 5 active keys. If you're near that limit, remove the old key before creating a new one to avoid hitting the cap mid-rotation. ::: A common pattern is to rotate credentials without downtime. Here's how it works: 1. **Create a new credential** — Call CreateCredential to generate a fresh key pair 2. **Update your application** — Deploy the new key to your application or secrets manager 3. **Verify the new key works** — Make a test API call using the new credential 4. **Remove the old credential** — Call RemoveCredentials with the old `key_id` This ensures your application is never without a valid credential during the rotation. ## Code Examples :::info Before You Run These Examples These examples assume you already have an active session cookie from completing the [Authentication Guide][authenticating-requests]. Complete that guide first — a valid session is required for all the requests below. ::: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ```python import requests import json BASE_URL = "https://noon-api-gateway.noon.partners" # Assumes you are already authenticated (session cookie from login) session = requests.Session() # ... authenticate first using the steps in the Authentication guide ... # Create a new credential with IP whitelisting and expiry. # Identify the service account by user_code (preferred) OR channel_identifier. create_response = session.post( f"{BASE_URL}/identity/apiuser/v1/credential/create", json={ "user_code": "USR12345", # Alternatively, omit user_code and provide channel_identifier: # "channel_identifier": "my-integrator-prj12345-abc@p99999.idp.noon.partners", "whitelisted_ips": ["203.0.113.10"], "expires_at": "2026-12-31T23:59:59Z" }, headers={"User-Agent": "MyApp/1.0.0"} ) credential = create_response.json()["credential"] key_id = credential["key_id"] # Store the credential securely — the private key won't be available again. # The returned credential includes channel_identifier, which you can keep on # hand for future rotations if you don't want to track user_code separately. with open("service_account_key.json", "w") as f: json.dump(credential, f) print(f"Created credential with key_id: {key_id}") # Later, remove the credential when it's no longer needed. remove_response = session.post( f"{BASE_URL}/identity/apiuser/v1/credential/remove", json={ "user_code": "USR12345", # Or identify by channel_identifier instead: # "channel_identifier": credential["channel_identifier"], "key_id": key_id }, headers={"User-Agent": "MyApp/1.0.0"} ) print(f"Removed {remove_response.json()['credentials_removed']} credential(s)") ``` ```bash # Create a new credential curl -X POST "https://noon-api-gateway.noon.partners/identity/apiuser/v1/credential/create" \ -H "Content-Type: application/json" \ -H "User-Agent: MyApp/1.0.0" \ -b cookies.txt \ -d '{ "user_code": "USR12345", "whitelisted_ips": ["203.0.113.10"], "expires_at": "2026-12-31T23:59:59Z" }' # Remove a specific credential by key_id curl -X POST "https://noon-api-gateway.noon.partners/identity/apiuser/v1/credential/remove" \ -H "Content-Type: application/json" \ -H "User-Agent: MyApp/1.0.0" \ -b cookies.txt \ -d '{ "user_code": "USR12345", "key_id": "key-abc123" }' # Remove ALL active credentials for a service account curl -X POST "https://noon-api-gateway.noon.partners/identity/apiuser/v1/credential/remove" \ -H "Content-Type: application/json" \ -H "User-Agent: MyApp/1.0.0" \ -b cookies.txt \ -d '{ "user_code": "USR12345" }' # Same operation, but identifying the account by channel_identifier # (e.g. the value returned in the OAuth token-exchange `result`). curl -X POST "https://noon-api-gateway.noon.partners/identity/apiuser/v1/credential/remove" \ -H "Content-Type: application/json" \ -H "User-Agent: MyApp/1.0.0" \ -b cookies.txt \ -d '{ "channel_identifier": "my-integrator-prj12345-abc@p99999.idp.noon.partners" }' ``` ## Limits | Limit | Value | |-------|-------| | Maximum keys per service account | 5 | | Maximum whitelisted IPs per credential | 10 | ## Error Handling Common error scenarios for **CreateCredential**: | Error | Description | Solution | |-------|-------------|----------| | `User not found` | Neither the `user_code` nor the `channel_identifier` matches an existing service account | Verify the value you provided. For OAuth-created accounts, the `channel_identifier` from the token exchange `result` is a drop-in option when you don't have the `user_code` | | `Either user_code or channel_identifier is required` | Both fields were omitted from the request | Provide one of the two — see [Identifying the Target Service Account](#identifying-the-target-service-account) | | `User is not active` | The service account has been deactivated | Reactivate the account through the noon Partner portal, or create a new service account | | `Cannot regenerate credentials for this user` | The `user_code` belongs to a non-service account | This API only works with service accounts. Check that you're using the correct `user_code` | | `Not allowed to regenerate credentials for this user` | Your session is referencing a different project than that of the target service account | You can only manage credentials by referencing the correct project | | `apijwt key quota exceeded for the account` | The service account already has the maximum number of active keys (default: 5) | Remove unused keys before creating new ones, or request a quota increase | | `Maximum 10 whitelisted IPs allowed` | More than 10 IPs were provided in `whitelisted_ips` | Reduce the list to 10 or fewer IP addresses | | Invalid `expires_at` format | The `expires_at` timestamp is not in valid ISO 8601 format | Use the format: `2026-12-31T23:59:59Z` | Common error scenarios for **RemoveCredentials**: | Error | Description | Solution | |-------|-------------|----------| | `User does not exist` | Neither the `user_code` nor the `channel_identifier` matches an existing service account | Verify the value you provided | | `Either user_code or channel_identifier is required` | Both fields were omitted from the request | Provide one of the two — see [Identifying the Target Service Account](#identifying-the-target-service-account) | | `Cannot remove channels for this user` | The `user_code` belongs to a non-service account | This API only works with service accounts. Check that you're using the correct `user_code` | | `Not allowed to remove channels for this user` | Your session is referencing a different project than that of the target service account | You can only manage credentials by referencing the correct project | :::tip RemoveCredentials is lenient by design — if the `user_code` is valid but there are no active credentials to remove, the API returns `success: true` with `credentials_removed: 0` rather than an error. ::: ## Security Best Practices - **Rotate credentials regularly** — Don't let keys live forever. Set up a rotation schedule (e.g., every 30–90 days) and use the create/remove flow to cycle keys. Request quota increases if you have reached the maximum number of keys allowed. - **Use IP whitelisting** — If your application runs on known infrastructure, whitelist those IPs. This limits the blast radius if a key is leaked. - **Set expiry dates** — Use `expires_at` as a safety net. Even if you forget to rotate a key, it will expire automatically. - **Don't keep unused keys active** — If a key is no longer in use, remove it. Fewer active keys means a smaller attack surface. - **Store private keys in a secrets manager** — Avoid storing keys as plain text files on disk or in environment variables. Use a dedicated secrets manager for production deployments. ## Next Steps - See the [API User API Reference][apiuser-create-credential-api] for full request/response details and schema documentation - Learn about [Authenticating Your Requests][authenticating-requests] using the credentials you create - See [Getting Your Credentials][getting-credentials] for creating service accounts via the portal