Whisper Money

Search the documentation

Search pages and sections.

Savings goals

A savings goal is tracked through a label: its contributions are the transactions tagged with it. A one_off goal saves towards a total, optionally by a date. A monthly goal starts over every calendar month with a target that is a fixed amount or an income_rate, a share of the average income of the three previous complete months, frozen when the month opens. A goal's kind is fixed once created. Archiving and deleting goals happen in the Whisper Money app.

The example requests read your personal token from $WHISPER_MONEY_TOKEN: run export WHISPER_MONEY_TOKEN=wm_api_your_token_here before pasting them.

List savings goals

GET /api/v1/savings-goals

Your goals in a space, newest first, archived ones included. As in the app, another member's goals are not listed.

Scope: read. A personal token needs api:read; an OAuth token needs api:use.

Parameters

Name In Type Required Description
limit query integer No How many objects to return, between 1 and 100. Defaults to 25.
starting_after query string (uuid) No An object id: return the page that follows it. Pass the last id of the page you have.
ending_before query string (uuid) No An object id: return the page that precedes it. Pass the first id of the page you have. Cannot be combined with starting_after.
space query string (uuid) No The id of the space to work in (see GET /spaces). Defaults to your personal space. On writes it may also be sent in the JSON body.

Response

200

Field Type Description
data array of SavingsGoal
data[].id string (uuid) The goal's id.
data[].space_id string (uuid) or null The id of the space the goal is in.
data[].name string The goal's name, which is also the name of its label.
data[].kind "one_off" | "monthly" one_off to reach a total once, monthly to save towards a target every calendar month. Fixed once created.
data[].label_id string (uuid) or null The id of the goal's label. A transaction counts towards the goal once it carries this label: set it with PUT /api/v1/transactions/{id}/labels.
data[].currency string The currency of the goal's amounts: the currency of the account of the member who created it, as an ISO 4217 code.
data[].target_amount integer or null One-off goals: the total to reach. null on a monthly goal.
data[].initial_amount integer or null One-off goals: what was already saved when the goal was created. null on a monthly goal.
data[].target_date string (date) or null One-off goals: the day to reach the total by, or null for none.
data[].progress object or null One-off goals: where the goal stands. saved and target are amounts, percentage how much of the target is saved, rate_per_day what was added per day since the goal started, status (completed, ahead, on_track or behind, null without a target date) its pace against the target date, expected_today what that pace asks to have saved by now, estimated_date when the current rate reaches the target and required_per_month what is left to save each month to make the target date. null on a monthly goal.
data[].progress.saved integer
data[].progress.target integer
data[].progress.percentage number
data[].progress.target_date string or null
data[].progress.rate_per_day number
data[].progress.expected_today integer or null
data[].progress.status string or null
data[].progress.estimated_date string or null
data[].progress.required_per_month integer or null
data[].monthly_target_type "amount" | "income_rate" | null Monthly goals: amount for a fixed sum each month, income_rate for a share of the average income of the three previous complete months, frozen when the month opens. null on a one-off goal.
data[].monthly_target_amount integer or null Monthly goals with an amount target: the sum to save each month. null otherwise.
data[].monthly_target_rate number or null Monthly goals with an income_rate target: the percentage of income to save each month. null otherwise.
data[].notify_on_month_end_reminder boolean or null Monthly goals: whether you get an email 5 days before the month ends while the month is short of its target. null on a one-off goal.
data[].auto_tag_account_id string (uuid) or null Monthly goals: the savings account whose incoming transfers an automation rule tags to this goal, or null for none.
data[].monthly object or null Monthly goals: each month judged against its own target, with nothing carried over between months. current is the month in progress (or null), with what is remaining and the days_left; history lists every month since the goal was created, oldest first. Each month has its target, saved, signed difference, income_base (the income an income_rate target was worked out from) and status: met, missed, in_progress, partial (the goal was created in the month's last 5 days) or archived (the goal was archived during it). A partial or archived month has no verdict and is left out of months_met, months_closed, streak, best_streak and cumulative_difference. A transaction tagged late recalculates the month it belongs to. null on a one-off goal.
data[].monthly.current object or null
data[].monthly.current.month string
data[].monthly.current.target integer
data[].monthly.current.saved integer
data[].monthly.current.difference integer
data[].monthly.current.status string
data[].monthly.current.income_base integer or null
data[].monthly.current.remaining integer
data[].monthly.current.days_left integer
data[].monthly.history array of object
data[].monthly.history[].month string
data[].monthly.history[].target integer
data[].monthly.history[].saved integer
data[].monthly.history[].difference integer
data[].monthly.history[].status string
data[].monthly.history[].income_base integer or null
data[].monthly.months_met integer
data[].monthly.months_closed integer
data[].monthly.cumulative_difference integer
data[].monthly.streak integer
data[].monthly.best_streak integer
data[].archived_at string (date-time) or null When the goal was archived in the app, or null. An archived goal keeps what it saved but can no longer change.
data[].created_at string (date-time) When the goal was created.
data[].updated_at string (date-time) or null When the goal was last changed.
has_more boolean Whether more objects exist past this page, in the direction being walked.

Example request

curl https://whisper.money/api/v1/savings-goals \
  -H "Authorization: Bearer $WHISPER_MONEY_TOKEN"

Example response

{
    "data": [
        {
            "id": "0199a3fb-2c3d-7e4f-8a5b-6c7d8e9f0a12",
            "space_id": "0199a3f2-6c1e-7b4d-9a8e-2f5c1d3b4a60",
            "name": "Emergency fund",
            "kind": "monthly",
            "label_id": "0199a3f6-3c4d-7e5f-9a6b-7c8d9e0f1a23",
            "currency": "EUR",
            "target_amount": null,
            "initial_amount": null,
            "target_date": null,
            "progress": null,
            "monthly_target_type": "amount",
            "monthly_target_amount": 30000,
            "monthly_target_rate": null,
            "notify_on_month_end_reminder": true,
            "auto_tag_account_id": "0199a3f3-7d2e-7c5f-8b9a-3e6d2c4b5a71",
            "monthly": {
                "current": {
                    "month": "2026-10",
                    "target": 30000,
                    "saved": 25000,
                    "difference": -5000,
                    "status": "in_progress",
                    "income_base": null,
                    "remaining": 5000,
                    "days_left": 29
                },
                "history": [
                    {
                        "month": "2026-09",
                        "target": 30000,
                        "saved": 32000,
                        "difference": 2000,
                        "status": "met",
                        "income_base": null
                    }
                ],
                "months_met": 1,
                "months_closed": 1,
                "cumulative_difference": 2000,
                "streak": 1,
                "best_streak": 1
            },
            "archived_at": null,
            "created_at": "2026-09-10T09:30:00+00:00",
            "updated_at": "2026-10-03T18:05:00+00:00"
        }
    ],
    "has_more": true
}

Errors

Status When
400 A malformed request: an unknown pagination cursor (invalid_cursor) or an invalid Idempotency-Key (invalid_idempotency_key).
401 No bearer token, or one that is unknown, expired or revoked.
403 The token lacks the scope (insufficient_scope), the account has no Pro plan (plan_required, with a subscribe_url) or it is a shared demo account (shared_account).
422 The request failed validation.
429 The rate limit is exhausted. Wait the seconds in Retry-After.

Create a savings goal

POST /api/v1/savings-goals

Creates the goal in the space, with a label of the same name to tag its contributions with. A one-off goal needs target_amount; a monthly goal needs monthly_target_type with its monthly_target_amount or monthly_target_rate, and its first month opens right away. The fields of the other kind are refused. A monthly goal given auto_tag_account_id also gets an automation rule, listed with the others, that tags the transfers into that savings account to the goal, the ones already there since the start of this month included. The rule goes last in your order, so an older rule that matches the same transfers keeps them.

Scope: write. A personal token needs api:write (a read & write token); an OAuth token needs api:use.

Parameters

Name In Type Required Description
space query string (uuid) No The id of the space to work in (see GET /spaces). Defaults to your personal space. On writes it may also be sent in the JSON body.
Idempotency-Key header string No Any unique string up to 255 characters. Retrying with the same key within 24 hours returns the first response instead of repeating the request.

Request body

Field Type Required Description
name string Yes The goal's name. It also names the label its contributions are tagged with, so it must not clash with one of your labels.
kind SavingsGoalKind Yes one_off to reach a total once, monthly to save towards a target every calendar month. Fixed once created.
auto_tag_account_id string (uuid) or null No Monthly goals: the id of one of your savings accounts in the goal's space. A new automation rule then tags the transfers into it to this goal, from the start of this month on. An account can feed only one running monthly goal.
target_amount integer No One-off goals: the total to reach, in the minor units of your account's currency.
initial_amount integer No One-off goals: what is already saved towards it, in minor units. Defaults to 0.
target_date string (date) or null No One-off goals: the day to reach the total by, YYYY-MM-DD; in the future for a new goal. null clears it.
monthly_target_type MonthlyTargetType No Monthly goals: amount for a fixed sum each month, income_rate for a share of the average income of the three previous complete months. Required on a new monthly goal, and on an edit alongside monthly_target_amount or monthly_target_rate; switching type clears the other value.
monthly_target_amount integer or null No Monthly goals with an amount target: the sum to save each month, in minor units.
monthly_target_rate number or null No Monthly goals with an income_rate target: the percentage of income to save each month, above 0 and up to 100, two decimals at most.
notify_on_month_end_reminder boolean No Monthly goals: email a reminder 5 days before the month ends while the month is short of its target. A new goal defaults to your notification setting.

Response

201

Field Type Description
data SavingsGoal
data.id string (uuid) The goal's id.
data.space_id string (uuid) or null The id of the space the goal is in.
data.name string The goal's name, which is also the name of its label.
data.kind "one_off" | "monthly" one_off to reach a total once, monthly to save towards a target every calendar month. Fixed once created.
data.label_id string (uuid) or null The id of the goal's label. A transaction counts towards the goal once it carries this label: set it with PUT /api/v1/transactions/{id}/labels.
data.currency string The currency of the goal's amounts: the currency of the account of the member who created it, as an ISO 4217 code.
data.target_amount integer or null One-off goals: the total to reach. null on a monthly goal.
data.initial_amount integer or null One-off goals: what was already saved when the goal was created. null on a monthly goal.
data.target_date string (date) or null One-off goals: the day to reach the total by, or null for none.
data.progress object or null One-off goals: where the goal stands. saved and target are amounts, percentage how much of the target is saved, rate_per_day what was added per day since the goal started, status (completed, ahead, on_track or behind, null without a target date) its pace against the target date, expected_today what that pace asks to have saved by now, estimated_date when the current rate reaches the target and required_per_month what is left to save each month to make the target date. null on a monthly goal.
data.progress.saved integer
data.progress.target integer
data.progress.percentage number
data.progress.target_date string or null
data.progress.rate_per_day number
data.progress.expected_today integer or null
data.progress.status string or null
data.progress.estimated_date string or null
data.progress.required_per_month integer or null
data.monthly_target_type "amount" | "income_rate" | null Monthly goals: amount for a fixed sum each month, income_rate for a share of the average income of the three previous complete months, frozen when the month opens. null on a one-off goal.
data.monthly_target_amount integer or null Monthly goals with an amount target: the sum to save each month. null otherwise.
data.monthly_target_rate number or null Monthly goals with an income_rate target: the percentage of income to save each month. null otherwise.
data.notify_on_month_end_reminder boolean or null Monthly goals: whether you get an email 5 days before the month ends while the month is short of its target. null on a one-off goal.
data.auto_tag_account_id string (uuid) or null Monthly goals: the savings account whose incoming transfers an automation rule tags to this goal, or null for none.
data.monthly object or null Monthly goals: each month judged against its own target, with nothing carried over between months. current is the month in progress (or null), with what is remaining and the days_left; history lists every month since the goal was created, oldest first. Each month has its target, saved, signed difference, income_base (the income an income_rate target was worked out from) and status: met, missed, in_progress, partial (the goal was created in the month's last 5 days) or archived (the goal was archived during it). A partial or archived month has no verdict and is left out of months_met, months_closed, streak, best_streak and cumulative_difference. A transaction tagged late recalculates the month it belongs to. null on a one-off goal.
data.monthly.current object or null
data.monthly.current.month string
data.monthly.current.target integer
data.monthly.current.saved integer
data.monthly.current.difference integer
data.monthly.current.status string
data.monthly.current.income_base integer or null
data.monthly.current.remaining integer
data.monthly.current.days_left integer
data.monthly.history array of object
data.monthly.history[].month string
data.monthly.history[].target integer
data.monthly.history[].saved integer
data.monthly.history[].difference integer
data.monthly.history[].status string
data.monthly.history[].income_base integer or null
data.monthly.months_met integer
data.monthly.months_closed integer
data.monthly.cumulative_difference integer
data.monthly.streak integer
data.monthly.best_streak integer
data.archived_at string (date-time) or null When the goal was archived in the app, or null. An archived goal keeps what it saved but can no longer change.
data.created_at string (date-time) When the goal was created.
data.updated_at string (date-time) or null When the goal was last changed.

Example request

curl -X POST https://whisper.money/api/v1/savings-goals \
  -H "Authorization: Bearer $WHISPER_MONEY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Emergency fund",
    "kind": "one_off",
    "target_amount": 1000000,
    "initial_amount": 50000,
    "target_date": "2027-06-30",
    "monthly_target_amount": 30000,
    "monthly_target_rate": 15,
    "notify_on_month_end_reminder": true
}'

Example response

{
    "data": {
        "id": "0199a3fb-2c3d-7e4f-8a5b-6c7d8e9f0a12",
        "space_id": "0199a3f2-6c1e-7b4d-9a8e-2f5c1d3b4a60",
        "name": "Emergency fund",
        "kind": "monthly",
        "label_id": "0199a3f6-3c4d-7e5f-9a6b-7c8d9e0f1a23",
        "currency": "EUR",
        "target_amount": null,
        "initial_amount": null,
        "target_date": null,
        "progress": null,
        "monthly_target_type": "amount",
        "monthly_target_amount": 30000,
        "monthly_target_rate": null,
        "notify_on_month_end_reminder": true,
        "auto_tag_account_id": "0199a3f3-7d2e-7c5f-8b9a-3e6d2c4b5a71",
        "monthly": {
            "current": {
                "month": "2026-10",
                "target": 30000,
                "saved": 25000,
                "difference": -5000,
                "status": "in_progress",
                "income_base": null,
                "remaining": 5000,
                "days_left": 29
            },
            "history": [
                {
                    "month": "2026-09",
                    "target": 30000,
                    "saved": 32000,
                    "difference": 2000,
                    "status": "met",
                    "income_base": null
                }
            ],
            "months_met": 1,
            "months_closed": 1,
            "cumulative_difference": 2000,
            "streak": 1,
            "best_streak": 1
        },
        "archived_at": null,
        "created_at": "2026-09-10T09:30:00+00:00",
        "updated_at": "2026-10-03T18:05:00+00:00"
    }
}

Errors

Status When
400 A malformed request: an unknown pagination cursor (invalid_cursor) or an invalid Idempotency-Key (invalid_idempotency_key).
401 No bearer token, or one that is unknown, expired or revoked.
403 The token lacks the scope (insufficient_scope), the account has no Pro plan (plan_required, with a subscribe_url) or it is a shared demo account (shared_account).
409 A request with the same Idempotency-Key is still being processed.
422 The request failed validation.
429 The rate limit is exhausted. Wait the seconds in Retry-After.

Retrieve a savings goal

GET /api/v1/savings-goals/{savings_goal}

Scope: read. A personal token needs api:read; an OAuth token needs api:use.

Parameters

Name In Type Required Description
savings_goal path string (uuid) Yes The goal's id.
space query string (uuid) No The id of the space to work in (see GET /spaces). Defaults to your personal space. On writes it may also be sent in the JSON body.

Response

200

Field Type Description
data SavingsGoal
data.id string (uuid) The goal's id.
data.space_id string (uuid) or null The id of the space the goal is in.
data.name string The goal's name, which is also the name of its label.
data.kind "one_off" | "monthly" one_off to reach a total once, monthly to save towards a target every calendar month. Fixed once created.
data.label_id string (uuid) or null The id of the goal's label. A transaction counts towards the goal once it carries this label: set it with PUT /api/v1/transactions/{id}/labels.
data.currency string The currency of the goal's amounts: the currency of the account of the member who created it, as an ISO 4217 code.
data.target_amount integer or null One-off goals: the total to reach. null on a monthly goal.
data.initial_amount integer or null One-off goals: what was already saved when the goal was created. null on a monthly goal.
data.target_date string (date) or null One-off goals: the day to reach the total by, or null for none.
data.progress object or null One-off goals: where the goal stands. saved and target are amounts, percentage how much of the target is saved, rate_per_day what was added per day since the goal started, status (completed, ahead, on_track or behind, null without a target date) its pace against the target date, expected_today what that pace asks to have saved by now, estimated_date when the current rate reaches the target and required_per_month what is left to save each month to make the target date. null on a monthly goal.
data.progress.saved integer
data.progress.target integer
data.progress.percentage number
data.progress.target_date string or null
data.progress.rate_per_day number
data.progress.expected_today integer or null
data.progress.status string or null
data.progress.estimated_date string or null
data.progress.required_per_month integer or null
data.monthly_target_type "amount" | "income_rate" | null Monthly goals: amount for a fixed sum each month, income_rate for a share of the average income of the three previous complete months, frozen when the month opens. null on a one-off goal.
data.monthly_target_amount integer or null Monthly goals with an amount target: the sum to save each month. null otherwise.
data.monthly_target_rate number or null Monthly goals with an income_rate target: the percentage of income to save each month. null otherwise.
data.notify_on_month_end_reminder boolean or null Monthly goals: whether you get an email 5 days before the month ends while the month is short of its target. null on a one-off goal.
data.auto_tag_account_id string (uuid) or null Monthly goals: the savings account whose incoming transfers an automation rule tags to this goal, or null for none.
data.monthly object or null Monthly goals: each month judged against its own target, with nothing carried over between months. current is the month in progress (or null), with what is remaining and the days_left; history lists every month since the goal was created, oldest first. Each month has its target, saved, signed difference, income_base (the income an income_rate target was worked out from) and status: met, missed, in_progress, partial (the goal was created in the month's last 5 days) or archived (the goal was archived during it). A partial or archived month has no verdict and is left out of months_met, months_closed, streak, best_streak and cumulative_difference. A transaction tagged late recalculates the month it belongs to. null on a one-off goal.
data.monthly.current object or null
data.monthly.current.month string
data.monthly.current.target integer
data.monthly.current.saved integer
data.monthly.current.difference integer
data.monthly.current.status string
data.monthly.current.income_base integer or null
data.monthly.current.remaining integer
data.monthly.current.days_left integer
data.monthly.history array of object
data.monthly.history[].month string
data.monthly.history[].target integer
data.monthly.history[].saved integer
data.monthly.history[].difference integer
data.monthly.history[].status string
data.monthly.history[].income_base integer or null
data.monthly.months_met integer
data.monthly.months_closed integer
data.monthly.cumulative_difference integer
data.monthly.streak integer
data.monthly.best_streak integer
data.archived_at string (date-time) or null When the goal was archived in the app, or null. An archived goal keeps what it saved but can no longer change.
data.created_at string (date-time) When the goal was created.
data.updated_at string (date-time) or null When the goal was last changed.

Example request

curl https://whisper.money/api/v1/savings-goals/0199a3f2-6c1e-7b4d-9a8e-2f5c1d3b4a60 \
  -H "Authorization: Bearer $WHISPER_MONEY_TOKEN"

Example response

{
    "data": {
        "id": "0199a3fb-2c3d-7e4f-8a5b-6c7d8e9f0a12",
        "space_id": "0199a3f2-6c1e-7b4d-9a8e-2f5c1d3b4a60",
        "name": "Emergency fund",
        "kind": "monthly",
        "label_id": "0199a3f6-3c4d-7e5f-9a6b-7c8d9e0f1a23",
        "currency": "EUR",
        "target_amount": null,
        "initial_amount": null,
        "target_date": null,
        "progress": null,
        "monthly_target_type": "amount",
        "monthly_target_amount": 30000,
        "monthly_target_rate": null,
        "notify_on_month_end_reminder": true,
        "auto_tag_account_id": "0199a3f3-7d2e-7c5f-8b9a-3e6d2c4b5a71",
        "monthly": {
            "current": {
                "month": "2026-10",
                "target": 30000,
                "saved": 25000,
                "difference": -5000,
                "status": "in_progress",
                "income_base": null,
                "remaining": 5000,
                "days_left": 29
            },
            "history": [
                {
                    "month": "2026-09",
                    "target": 30000,
                    "saved": 32000,
                    "difference": 2000,
                    "status": "met",
                    "income_base": null
                }
            ],
            "months_met": 1,
            "months_closed": 1,
            "cumulative_difference": 2000,
            "streak": 1,
            "best_streak": 1
        },
        "archived_at": null,
        "created_at": "2026-09-10T09:30:00+00:00",
        "updated_at": "2026-10-03T18:05:00+00:00"
    }
}

Errors

Status When
401 No bearer token, or one that is unknown, expired or revoked.
403 The token lacks the scope (insufficient_scope), the account has no Pro plan (plan_required, with a subscribe_url) or it is a shared demo account (shared_account).
404 No object with that id exists, or it belongs to someone else.
422 The request failed validation.
429 The rate limit is exhausted. Wait the seconds in Retry-After.

Update a savings goal

PATCH /api/v1/savings-goals/{savings_goal}

Changes only the fields you send, and only the fields of the goal's kind: the others, and kind itself, are refused. A new name renames the goal's label too. A new monthly target applies to the month in progress and later ones, never to the months already over. Only the member of the space who created the goal can change it, and an archived goal can no longer change: 422 under savings_goal.

Scope: write. A personal token needs api:write (a read & write token); an OAuth token needs api:use.

Parameters

Name In Type Required Description
savings_goal path string (uuid) Yes The goal's id.
space query string (uuid) No The id of the space to work in (see GET /spaces). Defaults to your personal space. On writes it may also be sent in the JSON body.

Request body

Field Type Required Description
name string No The goal's name. It also names the label its contributions are tagged with, so it must not clash with one of your labels.
target_amount integer No One-off goals: the total to reach, in the minor units of your account's currency.
initial_amount integer No One-off goals: what is already saved towards it, in minor units. Defaults to 0.
target_date string (date) or null No One-off goals: the day to reach the total by, YYYY-MM-DD; in the future for a new goal. null clears it.
monthly_target_type MonthlyTargetType No Monthly goals: amount for a fixed sum each month, income_rate for a share of the average income of the three previous complete months. Required on a new monthly goal, and on an edit alongside monthly_target_amount or monthly_target_rate; switching type clears the other value.
monthly_target_amount integer or null No Monthly goals with an amount target: the sum to save each month, in minor units.
monthly_target_rate number or null No Monthly goals with an income_rate target: the percentage of income to save each month, above 0 and up to 100, two decimals at most.
notify_on_month_end_reminder boolean No Monthly goals: email a reminder 5 days before the month ends while the month is short of its target. A new goal defaults to your notification setting.

Response

200

Field Type Description
data SavingsGoal
data.id string (uuid) The goal's id.
data.space_id string (uuid) or null The id of the space the goal is in.
data.name string The goal's name, which is also the name of its label.
data.kind "one_off" | "monthly" one_off to reach a total once, monthly to save towards a target every calendar month. Fixed once created.
data.label_id string (uuid) or null The id of the goal's label. A transaction counts towards the goal once it carries this label: set it with PUT /api/v1/transactions/{id}/labels.
data.currency string The currency of the goal's amounts: the currency of the account of the member who created it, as an ISO 4217 code.
data.target_amount integer or null One-off goals: the total to reach. null on a monthly goal.
data.initial_amount integer or null One-off goals: what was already saved when the goal was created. null on a monthly goal.
data.target_date string (date) or null One-off goals: the day to reach the total by, or null for none.
data.progress object or null One-off goals: where the goal stands. saved and target are amounts, percentage how much of the target is saved, rate_per_day what was added per day since the goal started, status (completed, ahead, on_track or behind, null without a target date) its pace against the target date, expected_today what that pace asks to have saved by now, estimated_date when the current rate reaches the target and required_per_month what is left to save each month to make the target date. null on a monthly goal.
data.progress.saved integer
data.progress.target integer
data.progress.percentage number
data.progress.target_date string or null
data.progress.rate_per_day number
data.progress.expected_today integer or null
data.progress.status string or null
data.progress.estimated_date string or null
data.progress.required_per_month integer or null
data.monthly_target_type "amount" | "income_rate" | null Monthly goals: amount for a fixed sum each month, income_rate for a share of the average income of the three previous complete months, frozen when the month opens. null on a one-off goal.
data.monthly_target_amount integer or null Monthly goals with an amount target: the sum to save each month. null otherwise.
data.monthly_target_rate number or null Monthly goals with an income_rate target: the percentage of income to save each month. null otherwise.
data.notify_on_month_end_reminder boolean or null Monthly goals: whether you get an email 5 days before the month ends while the month is short of its target. null on a one-off goal.
data.auto_tag_account_id string (uuid) or null Monthly goals: the savings account whose incoming transfers an automation rule tags to this goal, or null for none.
data.monthly object or null Monthly goals: each month judged against its own target, with nothing carried over between months. current is the month in progress (or null), with what is remaining and the days_left; history lists every month since the goal was created, oldest first. Each month has its target, saved, signed difference, income_base (the income an income_rate target was worked out from) and status: met, missed, in_progress, partial (the goal was created in the month's last 5 days) or archived (the goal was archived during it). A partial or archived month has no verdict and is left out of months_met, months_closed, streak, best_streak and cumulative_difference. A transaction tagged late recalculates the month it belongs to. null on a one-off goal.
data.monthly.current object or null
data.monthly.current.month string
data.monthly.current.target integer
data.monthly.current.saved integer
data.monthly.current.difference integer
data.monthly.current.status string
data.monthly.current.income_base integer or null
data.monthly.current.remaining integer
data.monthly.current.days_left integer
data.monthly.history array of object
data.monthly.history[].month string
data.monthly.history[].target integer
data.monthly.history[].saved integer
data.monthly.history[].difference integer
data.monthly.history[].status string
data.monthly.history[].income_base integer or null
data.monthly.months_met integer
data.monthly.months_closed integer
data.monthly.cumulative_difference integer
data.monthly.streak integer
data.monthly.best_streak integer
data.archived_at string (date-time) or null When the goal was archived in the app, or null. An archived goal keeps what it saved but can no longer change.
data.created_at string (date-time) When the goal was created.
data.updated_at string (date-time) or null When the goal was last changed.

Example request

curl -X PATCH https://whisper.money/api/v1/savings-goals/0199a3f2-6c1e-7b4d-9a8e-2f5c1d3b4a60 \
  -H "Authorization: Bearer $WHISPER_MONEY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Emergency fund",
    "target_amount": 1000000,
    "initial_amount": 50000,
    "target_date": "2027-06-30",
    "monthly_target_amount": 30000,
    "monthly_target_rate": 15,
    "notify_on_month_end_reminder": true
}'

Example response

{
    "data": {
        "id": "0199a3fb-2c3d-7e4f-8a5b-6c7d8e9f0a12",
        "space_id": "0199a3f2-6c1e-7b4d-9a8e-2f5c1d3b4a60",
        "name": "Emergency fund",
        "kind": "monthly",
        "label_id": "0199a3f6-3c4d-7e5f-9a6b-7c8d9e0f1a23",
        "currency": "EUR",
        "target_amount": null,
        "initial_amount": null,
        "target_date": null,
        "progress": null,
        "monthly_target_type": "amount",
        "monthly_target_amount": 30000,
        "monthly_target_rate": null,
        "notify_on_month_end_reminder": true,
        "auto_tag_account_id": "0199a3f3-7d2e-7c5f-8b9a-3e6d2c4b5a71",
        "monthly": {
            "current": {
                "month": "2026-10",
                "target": 30000,
                "saved": 25000,
                "difference": -5000,
                "status": "in_progress",
                "income_base": null,
                "remaining": 5000,
                "days_left": 29
            },
            "history": [
                {
                    "month": "2026-09",
                    "target": 30000,
                    "saved": 32000,
                    "difference": 2000,
                    "status": "met",
                    "income_base": null
                }
            ],
            "months_met": 1,
            "months_closed": 1,
            "cumulative_difference": 2000,
            "streak": 1,
            "best_streak": 1
        },
        "archived_at": null,
        "created_at": "2026-09-10T09:30:00+00:00",
        "updated_at": "2026-10-03T18:05:00+00:00"
    }
}

Errors

Status When
401 No bearer token, or one that is unknown, expired or revoked.
403 The token lacks the scope (insufficient_scope), the account has no Pro plan (plan_required, with a subscribe_url) or it is a shared demo account (shared_account).
404 No object with that id exists, or it belongs to someone else.
422 The request failed validation.
429 The rate limit is exhausted. Wait the seconds in Retry-After.