Added
- Assign a resource to projects: a resource is shared by the whole workspace until it is assigned, after which only the projects it is assigned to may place workloads on it. Anything else is refused with
422.POST /api/v1/projects/{project_id}/resourceswithresource_idassigns one. Needs bothprojects:manageandresources:manage.DELETE /api/v1/projects/{project_id}/resources/{resource_id}returns it to the shared pool. Needs onlyprojects:manage— removing an assignment only ever widens access, and every workload already there stays legal.- Assigning is refused with
422while the resource still runs workloads from a project outside the resulting list. The error names those projects and their workload counts, except the ones you cannot reach, which it reduces to a count. Sendwith_hosting_projects: trueto assign the resource to those projects too and let the call through; when a blocking project is redacted this is the only way forward, since you cannot name a project you cannot see.
- Assignment on the resource response: every resource carries
assigned(boolean) andproject_ids(project UUIDs).assigned: falsemeans any project may deploy on it. Read both before offering aresource_id: the resource is usable whenassignedis false, or whenproject_idscontains the target project.project_idslists only projects you can access. - Project access: a project can be closed to a list of members and roles, returned as
restrictionson the project response. Empty means the project is open to the whole workspace.POST /api/v1/projects/{project_id}/restrictionswith exactly one ofuser_emailorrole_name; sending both or neither is a400. Adding the first entry is what closes the project.DELETE /api/v1/projects/{project_id}/restrictions/{id}removes one entry, where{id}is the numericidfrom therestrictionslist. Removing the last one reopens the project.- Both need
projects:manage, and are available on organization and enterprise workspaces only. The plan refusal is checked before the subject, so on a smaller plan you get the plan message even when the email is also wrong.
- Move a workload between projects:
project_idonPUT /api/v1/apps/{uuid},PUT /api/v1/static/{uuid},PUT /api/v1/services/{id},PUT /api/v1/stacks/{uuid}andPUT /api/v1/buckets/{uuid}moves it to another project while it keeps running on the same resource. Moving a stack moves every application and service in it. Moving needs manage-level permission (apps:manage,services:manage), not the operate level the rest of the update takes. - Buckets can belong to a project:
project_idis accepted onPOST /api/v1/bucketsandPUT /api/v1/buckets/{uuid}. It is never inferred — omit it and the bucket has no project, even in a workspace with exactly one. Sendnullon update to clear it. - Workspace API tokens: tokens created under Settings → Developers can now be scoped to a workspace instead of a person. A workspace token carries its own permission list and its own project list, and nothing else is consulted — not the role of whoever created it, not workspace ownership. It is pinned to its workspace (an
X-Workspace-Idnaming a different one is refused with403), cannot reach/api/v1/users/meor anything under it, and cannot be granted workspace administration, so it can never mint another token. Tokens are managed in the dashboard only; there is no endpoint for them.
Changed
- Webhook endpoints require
workspace:general, notworkspace:integrations. API tokens and webhooks moved to their own Developers settings tab, and the permission moved with them. - A project you cannot reach is invisible rather than forbidden. Restricted projects are absent from
GET /api/v1/projects, and so are their applications, static sites, services, stacks and buckets in their own listings. Addressing any of them by UUID returns404, never403. The same applies to resources assigned solely to projects you cannot reach; an unassigned resource stays visible to everyone. A short list is no longer proof that the workspace holds nothing else. - A
project_idyou cannot reach and one that does not exist answer identically:404with{"error": "Project not found"}, on every endpoint that accepts a move. Telling them apart would confirm a restricted project is there. Read this as “the destination could not be resolved” — the workload is untouched and still in its original project. - Creating or updating a resource no longer falls back to ownership.
PUT /api/v1/resources/{uuid}andPUT /api/v1/resources/{uuid}/labelspreviously allowed the user who created the resource through regardless of role; they now requireresources:operatelike every other caller.
Fixed
- The permission reference was wrong in both directions. It listed
apps:create,resource:view,resource:manageandworkspace:settings, none of which exist, and omitted everyservices:permission along withprojects:operateand five of the sevenworkspace:ones. A403can now be turned into the name of a permission that can actually be granted.
Added
- Static sites: a new resource type with its own endpoints under
/api/v1/static. A static site serves prebuilt HTML, CSS and JavaScript from object storage — there is no compute resource, no replicas, no ports and no environment variables. It is not an application and is not reachable through/api/v1/apps.GET /api/v1/staticandPOST /api/v1/staticto list and create.GET,PUTandDELETE /api/v1/static/{uuid}to read, rename or move between projects, and delete the site with its content and domains.PUT /api/v1/static/{uuid}/deploymentto update build and routing settings. Applied as a patch: fields you omit keep their stored value.source_typeis not accepted here.POST /api/v1/static/{uuid}/deploymentsto deploy — send a multipartarchive(zip, max 1 GB) for azipsite, or omit it to rebuild agithubsite. Returns409 Conflictwhile a deployment is in flight.GET /api/v1/static/{uuid}/deploymentsfor deployment history.POST /api/v1/static/{uuid}/deployments/{id}/cancelto stop a build that is still running. Onlygit_pushandgithubsites run a build; aziporsftpdeployment is a direct sync with nothing to interrupt.POST /api/v1/static/{uuid}/deployments/{id}/rollbackto republish the site from the commit that deployment shipped.githubonly — a static site produces no image, so there is no stored artifact to redeploy and the platform rebuilds that commit instead. Agit_pushsite is rolled back by pushing again, andzipandsftpdeployments carry no commit; both return422.GET /api/v1/static/{uuid}/filesto browse deployed content one directory level per call (prefix,limit,cursor). Read-only. Returns422while the site’s storage is still being provisioned.GET|POST /api/v1/static/{uuid}/domains,PUT|DELETE .../domains/{domain_uuid}andPOST .../domains/{domain_uuid}/verifyfor custom domains, in the same shape as an app’s.
- Static site content sources:
deployment_config.source_typeis required at creation and is one ofgithub,git_push,ziporsftp. It decides what gets provisioned and cannot be changed afterwards — switching means creating another site.githubandgit_pushare built for you with the generator auto-detected;build_command,output_dirandproject_pathoverride detection, andspa_moderewrites unknown paths to/index.html.zipandsftptake already-built output. - Connection details come back ready to use:
deployment_config.git_ssh_urlis the remote agit_pushsite is pushed to, and appears once its repository has been provisioned — poll for it before pushing.deployment_config.sftp_endpointis the completeuser@hosttarget for ansftpsite. Read both from the site response rather than assembling them from the name and region. - Static site names are exact: unlike applications, no random suffix is appended. The name you send is the name the site is served under, and a name already in use is rejected with
422rather than silently renamed. - Static sites are created in
eu-east-1only:regiononPOST /api/v1/staticaccepts that one value, and anything else — includingus-east-1, which remains valid for every other resource — is rejected with400. Omit the field and you geteu-east-1. The region is only where the content is stored; serving is region-less, so it does not affect how the site is reached. - Copy security settings when cloning:
POST /api/v1/apps/{uuid}/cloneacceptsclone_security(boolean, defaultfalse), which copies allowed connections and Basic Auth from the source application. The flag was previously undocumented. - Outbound webhooks: register an endpoint and the platform POSTs deployment events to it, so you no longer have to poll
GET /api/v1/apps/{uuid}/deploymentsto find out when a deploy finished. Every endpoint below requires theworkspace:integrationspermission.GET /api/v1/webhooksandPOST /api/v1/webhooksto list and create.GET,PUTandDELETE /api/v1/webhooks/{uuid}to read, update and remove one.PUTchanges only the fields you send.POST /api/v1/webhooks/{uuid}/testto send a test event and get the result back immediately.GET /api/v1/webhooks/{uuid}/deliveriesfor delivery history.POST /api/v1/webhooks/{uuid}/deliveries/{delivery_uuid}/retryto replay a delivery.
- Two event types to subscribe to:
deploy_startedwhen a deployment entersrunning, anddeploy_endedwhen it reachescompleted,failedorcancelled. There is no separate build event — on Miget the build and the deployment are one lifecycle, andbuild_idis the deployment UUID. - Static site deployments emit these events too, with
data.app_idholding the UUID thatGET /api/v1/staticreturns — the same UUID that scopes a webhook to a single site. Aziporsftpdeployment has no commit behind it, socommit_sha,commit_messageandbrancharrive asnull. - Deliveries follow the Standard Webhooks specification, so any Standard Webhooks library verifies them as-is. Every request carries
webhook-id,webhook-timestampandwebhook-signature, where the signature is an HMAC-SHA256 of{webhook-id}.{webhook-timestamp}.{body}, base64-encoded and prefixedv1,. Reject deliveries whose timestamp is more than a few minutes old — that is what makes a captured request non-replayable. - The signing secret is returned exactly once.
POST /api/v1/webhooksis the only response containingsecret; every other endpoint omits it, and there is no rotation endpoint. Store it when you create the webhook — to replace it, delete the webhook and create a new one. - Scope a webhook to specific apps with
app_uuids. Leave it empty and the webhook covers every app in the workspace, including apps created later. The apps must belong to the same workspace or the request is rejected with422. - Delivery history tells you whether your endpoint is actually working. Each entry carries
status(pending,deliveredorfailed),response_code, amessageholding the response body or the transport error, anattemptscount, andpayload— the exact JSON body that was sent, so you can compare it against what you received. There is one entry per event, updated in place across retries, and the last 50 events per webhook are kept. - Test an endpoint before it matters.
POST /api/v1/webhooks/{uuid}/testPOSTs a signed event immediately and returns the resulting delivery, so you can verify a URL and your signature verification without waiting for a real deployment. It works on a disabled webhook, and responds200whether or not your endpoint accepted it — readstatuson the returned delivery. - Your consumer needs to tolerate a
pingtype. The test event carries"type": "ping"with an emptydataobject, andpingis not one of the subscribable event types. A consumer that rejects unknown types will fail the test even though the endpoint is otherwise fine. Acceptpingexplicitly, or ignore types you do not recognise. - Failed deliveries are retried for about 24 hours, across 9 attempts spaced 30s, 2m, 8m, 30m, 1h, 3h, 8h and 12h apart with jitter. Make your endpoint idempotent and deduplicate on the event
id, which is stable across automatic retries and across manual replays. A failing endpoint is never disabled automatically. - The endpoint must be publicly reachable. A
urlpointing into private address space — loopback, RFC1918, link-local including169.254.169.254, CGNAT, or thelocalhost,.localand.internalhostnames — is rejected with422. The host is re-resolved before every delivery, so a name that later starts resolving to a private address stops being delivered to rather than being retried.
Fixed
- Deploying an unresolvable commit:
POST /api/v1/apps/{uuid}/deploywith acommit_shathat is not in the GitHub repository the application is configured with now returns422and names the commit, instead of reporting201 Application is being deployedand failing in the build several seconds later. Push the commit before deploying it, and pass a SHA from that same repository — one that is unpushed, on a fork, or removed by a force-push will not resolve. This check applies to applications deployed from GitHub; deploying the latest commit on a branch already behaved this way. - Unknown
parent_app_id:POST /api/v1/appswithdeployment_method: "parent_image"and aparent_app_idthat does not exist now returns422saying so. It previously returned500.
Added
- Git build settings:
POST /api/v1/appsandPUT /api/v1/apps/{uuid}/deploymentnow acceptproject_path,run_command,language,build_command,pre_deploy_command,post_deploy_command, anduse_dhiin thepublic_gitandgithubdeployment configuration. The same fields are returned indeployment_configon the app response. - Release-phase commands:
pre_deploy_commandruns once before a new release starts andpost_deploy_commandonce after a successful deploy. Usepre_deploy_commandfor database migrations (npx prisma migrate deploy,alembic upgrade head,bin/rails db:migrate) so they run once per release rather than on every replica boot. - Custom builder configuration:
builder: "custom"requireslanguageandbuild_command, which the API previously rejected. You can now select and configure the custom builder in the same request. - Monorepo builds: set
project_pathto the subdirectory holding the app and the build treats it as the root. - App URL on the app response:
POST /api/v1/appsandGET /api/v1/apps/{uuid}returnpublic_url, the address the app is served on. Thenameyou send gets a random suffix appended, so the URL is never the one you would have built from that name — read it back from the response. Custom domains are not included; they are listed underGET /api/v1/apps/{uuid}/domains. - Plan type on the plan object:
GET /api/v1/plansreturnsplan_type(devorpro) on each plan, so the two tiers can be told apart from the plan list itself.
Changed
- Deployment updates are a patch, not a replacement:
PUT /api/v1/apps/{uuid}/deploymentmergesdeployment_config_attributesover the stored configuration. Fields you omit keep their value, and a field sent as an empty string is cleared. Previously every omitted field was wiped, so a partial update could silently clear settings such as the repository URL. The method’s identifying fields are the exception and must still be sent on every request:repository_urlforpublic_git,repositoryandcredential_idforgithub,image_urlandtagforcontainer_registry. Sending a differentdeployment_methodstill builds the configuration from scratch, so supply every field that method needs. - Add-on creation validates its required fields:
POST /api/v1/apps/{uuid}/addonsnow rejects an incomplete request with400.labelis required.postgres_version,mysql_version, andvalkey_versionare each required for their matchingtype.mount_pointandstorage_accessare required for a standalonestorageadd-on — passservice_idinstead and both are inherited from that service. - Service creation validates its required fields:
POST /api/v1/servicesnow returns400whenpostgres_versionis missing forservice_type: "postgres", ormount_pointis missing forservice_type: "shared_storage". - Database versions are checked against a list: an unsupported version is rejected with
400instead of being accepted. Accepted values arepostgres_version:18,17,16,15,14,13;mysql_version:8.2,8.0;valkey_version:7,7.2.8.4was previously listed as a MySQL version but was never supported. plan_typeis no longer required when creating a resource:POST /api/v1/resourcesresolves the plan fromplan_code_namealone.plan_typeis still accepted for backward compatibility but is ignored. Pass acode_namefromGET /api/v1/plansverbatim — the values are opaque identifiers such asmiget_hobby_0, and they differ between environments.
Fixed
- App detail for
public_gitapps:GET /api/v1/apps/{uuid}returns the repository underrepository_url, matching the field you use to create and update it. This request previously failed for apps using thepublic_gitdeployment method. - Deployment update response:
PUT /api/v1/apps/{uuid}/deploymentreturns the deployment configuration it just saved. It previously returned a nulldeployment_config, so you had to re-fetch the app to see your own change. - App create response:
POST /api/v1/appsreturns thedeployment_configit just saved instead ofnull, so you can read back your own configuration without a follow-up request. - Storage add-on on a service without storage:
POST /api/v1/apps/{uuid}/addonswithtype: "storage"and aservice_idpointing at a service that has no storage now returns422explaining the problem. It previously returned500. - Rate-limited Git host: when a
public_gitrepository cannot be read because the Git host is rate-limiting the request, the error now says so and asks you to retry. It previously reported that the repository or branch does not exist, which sent you looking for a problem with a URL that was correct. - Branch with no commits: pointing a
public_gitdeployment at an empty branch now fails with a message naming the branch, instead of an unhelpful server error.
Added
- App internal URL & auth state:
GET /api/v1/apps/{uuid}now returnsinternal_url(the<service>.<resource>.<region>.migetapp.internal:5000address for app-to-app and addon connections,nulluntil a compute resource is assigned) andbasic_auth_enabled(whether HTTP Basic Auth is enforced at the ingress; credentials are never returned). - Deployment commit metadata: deployment records from
GET /api/v1/apps/{uuid}/deploymentsnow includecommit_sha,commit_message, andbranchfor git-based deployment methods (nullotherwise), so you can confirm which commit is live. - Cron run logs:
GET /api/v1/apps/{uuid}/cronjobs/{id}/stream_logsstreams the cron job’s most recent run logs over SSE (returns404until the job has run at least once).
Changed
- Deploy while busy:
POST /api/v1/apps/{uuid}/deploynow returns409 Conflict(previously a generic422) when a deployment is already in progress, with a message pointing atGET /api/v1/apps/{uuid}/deploymentsto poll before retrying.
Changed
- Scaling profile
within_resources: this field onPUT /api/v1/apps/{uuid}/scaling_profilewas never active, and its description did not say so. Scaling is always limited to the resource’s allocation; scaling beyond it is not implemented. The field is still accepted and requests need no change — the reference now states that it is ignored.
Changed
- Public Git deployment field: For
public_gitapps, thedeployment_configfield isrepository_url(a full HTTPS URL), notrepository. The OpenAPI spec now declaresrepository_urlonPOST /api/v1/appsandPUT /api/v1/apps/{uuid}/deployment.repositoryremains thegithubfield (owner/repoformat). Clients that were sendingrepositoryforpublic_gitshould switch torepository_url. - Switching to Git deployment:
PUT /api/v1/apps/{uuid}/deploymentnow documents thepublic_gitandgithubconfiguration bodies (previously onlycontainer_registry,parent_image, andkamalwere described). - Container image reference: Clarified that
image_urlmust be provided without a scheme and without a tag (e.g.docker.io/library/nginx); put the tag in the separatetagfield (defaults tolatest). - Deployment method naming: Corrected the documented enum for
deployment_methodtocontainer_registry(the valuedocker_registrynever existed).
Added
Deploy Button Deep Links
“Deploy to Miget” buttons can now open the deploy wizard prefilled from a source. Supported query parameters:
- Stack:
/stacks/new?repo=<url-or-slug>&branch=<branch>&path=<compose-path> - App (public Git):
/apps/new?repo=<repo-url>&branch=<branch> - App (container image):
/apps/new?image=<registry/image:tag>
Added
Docker Compose Stacks API
Deploy a multi-service application from a single
compose.yaml in a Git repository. Analyze a repo first, then create a stack that provisions every detected app and managed service together:- Analyze:
POST /api/v1/stacks/analyze(detects services and required env vars; creates nothing) - Create:
POST /api/v1/stacks - List / Get:
GET /api/v1/stacks·GET /api/v1/stacks/{uuid} - Update:
PUT /api/v1/stacks/{uuid}(label,compose_path) - Deploy:
POST /api/v1/stacks/{uuid}/deploy - Deployment config:
PUT /api/v1/stacks/{uuid}/deployment(branch, auto-deploy, repository) - Deployment history:
GET /api/v1/stacks/{uuid}/deployments·GET /api/v1/stacks/{uuid}/deployments/{id} - Delete:
DELETE /api/v1/stacks/{uuid}(cascades to the stack’s apps and services)
Git Credentials API
Read-only access to the workspace Git credentials (GitHub App installs / tokens) used to clone private repositories. The returned
uuid is what you pass as credential_id when creating a stack or a github/public_git app. Tokens are never returned.- List:
GET /api/v1/git_credentials - Get:
GET /api/v1/git_credentials/{uuid}
Changed
- App response now includes region:
GET /api/v1/apps/{uuid}now returns a nestedregionobject (id,name,code) alongside the app’s other fields.
Added
App Activity API
A new endpoint for retrieving the activity feed of an application:
- Get App Activity:
GET /api/v1/apps/{uuid}/activity(supportspageandlimitquery parameters for pagination)
Container Registry Credentials API
A workspace-scoped CRUD surface for managing registry credentials used by Dockerfile and container deployments:
- Create:
POST /workspaces/{workspace_id}/container_registry_credentials - Update:
PUT /workspaces/{workspace_id}/container_registry_credentials/{uuid} - Delete:
DELETE /workspaces/{workspace_id}/container_registry_credentials/{uuid}
Changed
- App Creation:
POST /api/v1/appsnow requires theresource_idparameter to identify the target Resource plan. The previousregion_idparameter has been removed because it was misleading on a fixed-capacity model. - App Domains Path: The path parameter on domain-scoped endpoints has been renamed from
uuidtodomain_uuidto disambiguate it from the parent app’suuid. This affects:GET /api/v1/apps/{uuid}/domains/{domain_uuid}PUT /api/v1/apps/{uuid}/domains/{domain_uuid}DELETE /api/v1/apps/{uuid}/domains/{domain_uuid}
- Replica Endpoints:
POST /api/v1/apps/{id}/addons/create_replicaandPOST /api/v1/services/{id}/create_replicanow return the fullAddonandServiceentities (previously returned a genericMessage). The response now includes the new replica’suuidandname. - Cron Job Delete:
DELETEon cron job endpoints now returns aMessagebody alongside the204status for consistency with other destroy endpoints. - Cron Job Entity: Renamed
statetostatus(running,stopped) and added two new required fields:last_job_name(name of the most recent run) andlast_job_status(e.g.running,complete,failed). Clients reading thestatefield should switch tostatus. - Region Entity: Now exposes a numeric
idfield in addition tonameandcode. Theidis required. - Container Image Overrides: App and addon deployment configs now accept
commandandargsarrays to override the image’sENTRYPOINTandCMD. Useful for images like Keycloak that print help on a bare run.
Added
Buckets API (S3-Compatible Object Storage)
A complete set of endpoints for managing S3-compatible storage buckets:
- List Buckets:
GET /api/v1/buckets - Create Bucket:
POST /api/v1/buckets - Get Bucket:
GET /api/v1/buckets`/{uuid}` - Update Bucket:
PUT /api/v1/buckets`/{uuid}` - Delete Bucket:
DELETE /api/v1/buckets`/{uuid}` - Regenerate Credentials:
POST /api/v1/buckets`/{uuid}`/regenerate_credentials - Update Policy:
PUT /api/v1/buckets`/{uuid}`/update_policy - Update ACL:
PUT /api/v1/buckets`/{uuid}`/update_acl
Bucket Objects API
File and folder management within buckets:
- List Objects:
GET /api/v1/buckets`/{uuid}`/objects/list - Get Upload URL:
POST /api/v1/buckets`/{uuid}`/objects/upload_url - Get Download URL:
POST /api/v1/buckets`/{uuid}`/objects/download_url - Create Folder:
POST /api/v1/buckets`/{uuid}`/objects/create_folder - Rename Object:
PUT /api/v1/buckets`/{uuid}`/objects/rename - Delete Object:
DELETE /api/v1/buckets`/{uuid}`/objects/destroy
Database Replication API
New endpoints for managing PostgreSQL streaming replication on both app addons and standalone services:
- Create Replica:
POST /api/v1/apps`/{id}`/addons/create_replicaandPOST /api/v1/services`/{id}`/create_replica - Promote Replica:
POST /api/v1/apps`/{id}`/addons/promote_replicaandPOST /api/v1/services`/{id}`/promote_replica - Promote External:
POST /api/v1/apps`/{id}`/addons/promote_externalandPOST /api/v1/services`/{id}`/promote_external - Enable Streaming Replication:
POST /api/v1/services`/{id}`/enable_streaming_replication - Disable Streaming Replication:
POST /api/v1/services`/{id}`/disable_streaming_replication
Changed
- Deploy Endpoint: The
POST /api/v1/apps`/{uuid}`/deployendpoint now supports deploying from GitHub repositories withgithub_urlandgithub_branchparameters, in addition to the existingcommit_shasupport.
Added
App Deployments API
A new set of endpoints for managing the application deployment lifecycle has been introduced:
- List Deployments:
GET /api/v1/apps`/{uuid}`/deployments - Get Deployment Details: “GET /api/v1/apps
/{uuid}/deployments/{id} - Get Build Logs:
GET /api/v1/apps`/{uuid}`/deployments`/{id}`/logs - Stream Build Logs:
GET /api/v1/apps`/{uuid}`/deployments`/{id}`/stream_logs - Cancel Deployment:
POST /api/v1/apps`/{uuid}`/deployments`/{id}`/cancel - Rollback Deployment:
POST /api/v1/apps`/{uuid}`/deployments`/{id}`/rollback
App Ports API
Full management of application network ports is now available:
- List Ports:
GET /api/v1/workspaces`/{ws}`/apps`/{app}`/ports - Create Port:
POST /api/v1/workspaces`/{ws}`/apps`/{app}`/ports - Delete Port: “DELETE /api/v1/workspaces
/{ws}/apps/{app}/ports/{id} - Expose Port Publicly:
PATCH /api/v1/workspaces`/{ws}`/apps`/{app}`/ports`/{id}`/expose_publicly - Make Port Private:
PATCH /api/v1/workspaces`/{ws}`/apps`/{app}`/ports`/{id}`/make_private
Add-on Password Rotation
The new
POST /api/v1/apps`/{uuid}`/addons`/{id}`/rotate_password endpoint allows for secure password rotation for supported add-ons, such as databases.Basic Authentication
The
PUT /api/v1/apps`/{uuid}`/security endpoint now supports basic_auth_enabled, basic_auth_username, and basic_auth_password parameters, allowing you to enable and configure Basic Authentication for your application.Changed
- Deployment Options: The
POST /api/v1/apps`/{uuid}`/deployendpoint now acceptscommit_shaandbranchparameters, enabling more precise deployments from Git repositories. - Builder Options: The
builderparameter inPOST /api/v1/appshas been expanded to supportauto(automatic buildpack detection) andcustom(language-specific builder) in addition todockerfile. - Resource Validation: The “PUT /api/v1/resources
/{uuid}endpoint now validates against plan downgrades to prevent assigning a plan with insufficient resources (CPU, RAM, storage). - Add-on Deletion Logic: The logic for deleting an add-on (“DELETE /api/v1/apps
/{uuid}/addons/{id}) has been refactored into a dedicated service object for improved maintainability.
Removed
- The
POST /api/v1/apps`/{uuid}`/sync_parent_imageendpoint has been removed.

