# Welcome to Looped (/applications) Looped is a suite of small, focused apps for running a service business. The apps are independent — use one or all — but they all share the same workspace, the same teammates, and a single subscription. ## The apps [#the-apps] * **[Looped Track](/applications/track)** — log time, track projects, run reports. * **[Looped Invoices](/applications/invoices)** — clients, line items, sent-by-email invoices with online payment. * **[Looped Pay](/applications/pay)** — the page your customers land on to pay an invoice. * **[Public API](/applications/api)** — read and write your team's data programmatically. You'll do most of the day-to-day in those apps. Everything *about* them — who's on your team, what plan you're on, your branding, your bank details — happens in **Manage**. ## How everything fits together [#how-everything-fits-together] Sign in once at [app.looped.sh](https://app.looped.sh) and you land in Manage — the home for your **team**. From the Manage header you can switch into any app: ``` app.looped.sh/ ← Manage (default) app.looped.sh/track ← Looped Track app.looped.sh/invoices ← Looped Invoices api.looped.sh ← Public API (interactive reference) pay.looped.sh/ ← Looped Pay (customer-facing) ``` A **team** is the unit you collaborate around. It owns your subscription, your members, your invoices, your time entries, and your settings. You can be in multiple teams — switch between them from the team picker in the header. ## What to read next [#what-to-read-next] If you're brand new, the **[Get Started](/applications/creating-a-team)** path walks you through creating a team, inviting your first teammate, and picking a plan. If you're already up and running and want to administer the workspace — billing, members, API keys — head to **[Manage](/applications/manage/roles)**. If you want to use a specific app, jump straight to its overview: * **[Looped Track](/applications/track)** * **[Looped Invoices](/applications/invoices/getting-started)** ## Getting help [#getting-help] * 💬 Join our [Discord](https://discord.gg/euwUfRt3nt) * 📧 Email [support@looped.sh](mailto:support@looped.sh) # How Looped is structured (/applications/concepts/platform) Most things in Looped revolve around a few simple ideas. Internalize these and the rest of the docs will read faster. ## Teams own everything [#teams-own-everything] Every piece of data — invoices, time entries, projects, clients, API keys — belongs to a **team**. When you sign in, Looped picks one of your teams as your *current* team and shows you that team's data. You can be in multiple teams (e.g. *Acme Studio* and *Personal*). Switch between them in the top-left team picker. ## Members and roles [#members-and-roles] A team has one or more **members**. Each member has a **role** that determines what they can do: | Role | Can do | | ---------- | ------------------------------------------------------------------------------------------------------ | | **OWNER** | Everything — including delete the team and transfer ownership | | **ADMIN** | Manage billing, invite/remove members, change settings (cannot delete the team or modify other OWNERs) | | **MEMBER** | Use the apps — log time, send invoices, etc. Cannot manage billing, members, or settings | A team always has at least one OWNER. The platform won't let the last OWNER leave or be demoted. ## One subscription per team [#one-subscription-per-team] Subscriptions are bought per team. Two plan styles: * **Looped One** — the bundle. Includes every app at one price. * **Single App** — pick one app (Track *or* Invoices). Subscriptions are priced **per seat**, where a seat = an active member. The seat count must always cover your members; adding a new member consumes a seat (and adds a new one if you're full). Lower the count by [removing members](/applications/manage/member-management#removing-a-member) first. ## Apps share auth and team context [#apps-share-auth-and-team-context] You sign in once. Everything else inherits that session: * **Manage** is the gateway. It hosts the auth, the team-management surface, and the navigation that fans out to the other apps. * **Track** and **Invoices** live as subpaths under the same domain (`app.looped.sh/track`, `app.looped.sh/invoices`). Switching between them keeps the same team context — no re-login. * **Pay** is *customer-facing*. When you send an invoice by email, your client gets a unique link to a Pay page where they can pay you. They don't sign in. * **The Public API** lives at `api.looped.sh`. Authenticate with an [API key](/applications/manage/api-keys) — no session needed. ## Currency lock [#currency-lock] Each team has a **currency** that's set when you create the team and locked the moment your first invoice is paid. After that point, the currency can't change — it lives on every past invoice. If you need to invoice in two currencies, [create a second team](/applications/creating-a-team). ## What lives where [#what-lives-where] | Thing | Lives in | Visible at | | ---------------------------------- | -------- | ----------------------------- | | Team name, currency, address, logo | Manage | *Settings* → *General* | | Billing & seats | Manage | *Billing* | | Members | Manage | *Members* | | API keys | Manage | *Settings* → *API Keys* | | Time entries, projects | Track | `/track` | | Invoices, clients | Invoices | `/invoices` | | Bank details (for invoices) | Manage | *Settings* → *General* → Bank | This model — one team, one subscription, multiple apps sharing the same identity — is what makes the platform feel small even though it does a lot. # Creating a team (/applications/creating-a-team) A team is the workspace your subscription, members, and data sit inside. You can have as many teams as you want — switch between them in the team picker. ## Your first team is automatic [#your-first-team-is-automatic] The first time you sign in to [app.looped.sh](https://app.looped.sh), Looped creates a team for you with your name (or your email's domain) and makes you the OWNER. You can rename it later from *Settings* → *General*. If that's the only team you'll ever need, you're done. Skip ahead to [Inviting team members](/applications/inviting-team-members). ## Adding another team [#adding-another-team] Open a second team if you want to: * Keep a personal team and a business team apart * Bill clients in different currencies (currency is per-team and [locks](/applications/concepts/platform#currency-lock) after the first paid invoice) * Run multiple businesses from one Looped account To create one: 1. Click the team picker (top-left, next to the Looped logo). 2. Click **+ Create Team** at the bottom of the dropdown. 3. Pick a name and click *Create*. The new team becomes your current team automatically. Everything you do — invoices, time entries, settings — applies only to it until you switch away. ## Switching teams [#switching-teams] Click the team picker → pick a team. Looped reloads with that team as your current context. Each team's data is fully isolated; nothing leaks across. ## Renaming, currency, address [#renaming-currency-address] Open *Settings* → *General* to update: * **Name** — what shows up in the team picker and on invoices * **Currency** — locks once an invoice is paid (see [Currency lock](/applications/concepts/platform#currency-lock)) * **VAT number** — surfaces on invoices * **Logo** — displayed on invoices and in your team's header * **Address** — used as the "From" address on invoices * **Bank details** — displayed at the bottom of invoices for EFT payments These details aren't required to use Looped, but they *are* required before you can [send an invoice](/applications/invoices/sending-invoices). The fastest way to fill them in is the [Quick Start wizard](/applications/team-settings#quick-start) on the dashboard, which extracts most of them from your existing website or a previous invoice PDF. ## Next steps [#next-steps] 1. [Invite your team members](/applications/inviting-team-members) 2. [Purchase a subscription](/applications/purchasing-subscriptions) to unlock the apps 3. [Configure team settings](/applications/team-settings) so your invoices look right # Inviting team members (/applications/inviting-team-members) Adding teammates to a Looped team gives them access to all of the team's data — invoices, time entries, settings — at the role you assign them. :::caution Inviting requires an **active subscription**. Free-plan teams can't invite. Buy a plan first via [Purchasing a subscription](/applications/purchasing-subscriptions). ::: ## Sending an invite [#sending-an-invite] 1. Sign in to [app.looped.sh](https://app.looped.sh) and switch to the team you want to invite into. 2. Click *Members* in the header. 3. Fill in the **Invite Member** form: * **Email** — your teammate's address * **Role** — pick MEMBER (default), ADMIN, or OWNER. See [Roles](/applications/manage/roles) for what each grants. 4. Click *Send Invite*. Looped emails them a one-click accept link and the invite shows up in the **Invites** tab as `Pending`. ## What your teammate sees [#what-your-teammate-sees] They get an email with a button that takes them to `/invites/accept/`. After signing in (or signing up) with the same email address you invited: * Click *Accept Invitation* → they join the team and land on its dashboard. * Click *Reject Invitation* → the invite is marked rejected and you'll need to re-invite if they change their mind. If they sign in with a *different* email, Looped surfaces a "wrong account" page asking them to sign out and try again with the right address. ## Sending a different way [#sending-a-different-way] If the email never arrived (spam, typo, etc.), you can grab the invite link directly: 1. Open the **Invites** tab on the Members page. 2. Click the **copy** icon on their invite row. 3. Paste the link into Slack, WhatsApp, Signal — wherever they'll see it. The link is the same URL the email contains; clicking it has the same effect. ## Cancelling a pending invite [#cancelling-a-pending-invite] 1. Open the **Invites** tab. 2. Click the **delete** (trash) icon on the row. If they try to accept after that, they'll see a "this invite is no longer valid" page. ## Invite expiry [#invite-expiry] Invites expire after **7 days**. After that, the link still routes to a valid page but the accept action is refused. If a teammate misses the window, send them a fresh invite — there's no resurrect-after-expiry option. ## Seats and inviting [#seats-and-inviting] Looped automatically grows your seat count when an invite is accepted, so you don't have to bump it manually first. The new charge is prorated. If your subscription doesn't have spare seats *and* you don't want auto-grow, [adjust seats first](/applications/manage/billing-details#changing-the-seat-count) — but in practice the auto-grow path is what most people want. ## Next steps [#next-steps] * See the [Roles](/applications/manage/roles) page for the full permission matrix. * Learn about [removing members or changing roles](/applications/manage/member-management). # Purchasing a subscription (/applications/purchasing-subscriptions) Your first team starts on the **Free plan** — enough to log in and look around, but not enough to invite teammates or send invoices. Buying a subscription unlocks the rest. ## Plans [#plans] Looped offers two plan styles: | Plan | Includes | When to pick | | -------------- | ----------------------------- | --------------------------------------------------------- | | **Looped One** | Every Looped app | You'll use more than one app, or expect to grow into them | | **Single App** | Just Track *or* just Invoices | You're sure you only need one | Both are priced **per seat**. Latest pricing lives at [looped.sh/pricing](https://looped.sh/pricing). ## Buying [#buying] 1. Sign in to [app.looped.sh](https://app.looped.sh) and switch to the team you want to subscribe. 2. Click *Billing* in the header. 3. Pick the plan and the number of seats you want. 4. Click *Start Subscription*. You'll be redirected to **Stripe Checkout** to enter card details. After paying, you're sent back to Looped and the apps unlock immediately. :::note Stripe handles all card data. Looped never sees or stores your card number. ::: ## Seats explained [#seats-explained] A "seat" = one active member of your team. * The seat count must always be **≥ the number of members in the team**. * Inviting a new member when you're full automatically increases your seat count by 1 (prorated charge). * To pay for fewer seats, [remove members first](/applications/manage/member-management#removing-a-member), then [lower the count](/applications/manage/billing-details#changing-the-seat-count). You can change seats any time from the billing page after the subscription is active. ## Switching plans [#switching-plans] Once you have an active subscription, the billing page replaces *Start Subscription* with **Manage Subscription**. Click that and you'll be sent to the **Stripe Customer Portal** where you can: * Switch between Single App and Looped One * Add or remove apps from a multi-app subscription * Update payment methods * See past invoices and download receipts * Cancel Plan upgrades take effect immediately (prorated). Plan downgrades take effect at the end of your current billing period. ## What happens at the end of a free trial / failed payment [#what-happens-at-the-end-of-a-free-trial--failed-payment] If a payment fails, your subscription enters `past_due` and the apps stop unlocking new actions until the payment goes through. Stripe retries automatically. Once paid, you're back to normal. If you cancel via the customer portal, the team returns to the Free plan when the current period ends. Your team and its data stay intact — you can re-subscribe later. ## Free vs. Paid summary [#free-vs-paid-summary] | Feature | Free | Active subscription | | -------------------- | ---- | ----------------------- | | Sign in | ✅ | ✅ | | Browse the dashboard | ✅ | ✅ | | Create a team | ✅ | ✅ | | Invite members | ❌ | ✅ | | Use Track / Invoices | ❌ | ✅ (depending on plan) | | Send invoices | ❌ | ✅ (Invoices/Looped One) | ## Next steps [#next-steps] * [Manage your billing details](/applications/manage/billing-details) once you're subscribed * [Set up team details](/applications/team-settings) so your invoices look right * [Send your first invoice](/applications/invoices/sending-invoices) # Team settings (/applications/team-settings) A handful of details about your team show up on every invoice you send. Configuring them once means they're correct everywhere. Open *Settings* from the header to find them. Most of them sit on the **General** page; bank details and address have their own sections. ## Quick Start [#quick-start] The fastest way to fill out the basics is the **Quick Start** card on your dashboard. Paste your business website's URL or upload an existing invoice PDF and Looped extracts: * Team name * Address * Currency * VAT number * Bank details (where present) Review what was extracted, edit anything that's wrong, and click *Save*. You'll skip a couple of forms. If Quick Start doesn't find what you need, fill the fields manually below. ## General [#general] | Field | What it's used for | | --------------- | -------------------------------------------------------------------------------------------------------------------------------- | | **Name** | Team picker, header, "From" on invoices | | **Friendly ID** | URL slug — used in some shared links and the team picker | | **Currency** | Set on every invoice. Locks once your first invoice is paid (see [Currency lock](/applications/concepts/platform#currency-lock)) | | **VAT number** | Surfaces on invoices in the "From" block | | **Logo** | Top-left of every invoice PDF | Editing these requires the OWNER role. ## Address [#address] Used as the "From" address on invoices and as your business location for tax purposes. Open *Settings* → *General* → Address. | Field | Required? | | --------------- | --------- | | Line 1 | Yes | | Line 2 / Line 3 | No | | Postal code | Yes | You can't [send an invoice](/applications/invoices/sending-invoices) without a valid address. ## Bank details [#bank-details] Displayed at the bottom of invoices for clients paying via EFT (or as a fallback if you don't have a payment provider set up). | Field | Notes | | -------------- | ------------------------------------------------------ | | Account holder | Your business name as it appears on the bank statement | | Account number | The account to receive funds | | Account type | Cheque, savings, etc. | | Bank name | E.g. *FNB*, *Standard Bank* | | Branch code | Bank's branch / SWIFT code | Bank details are optional — if you'd rather only accept card payments via Stripe / Yoco / PayFast, leave this blank and the section won't show on invoices. ## Logo [#logo] PNG, JPG, or WebP. Square images render best. Settings → *General* → drag-drop a file onto the avatar. ## Why this matters [#why-this-matters] A correctly-filled team settings page means: * Invoices reach clients with your branding * "From" addresses are valid (some jurisdictions require it on tax invoices) * Clients can pay via EFT if they prefer * Your team appears with the right name in everyone's team picker Unset fields show through as gaps on the invoice — better to fill them once than to find out at send time. ## Next steps [#next-steps] * Once your settings are right, [send your first invoice](/applications/invoices/sending-invoices) * Or [invite teammates](/applications/inviting-team-members) to start collaborating # API keys (/applications/manage/api-keys) API keys let scripts, services, and third-party integrations talk to Looped on behalf of your team. Every key is scoped to a single team and a set of permissions. The API keys page is OWNER/ADMIN only. Open it from *Settings* → *API Keys*. ## Creating a key [#creating-a-key] 1. Click *Create API Key*. 2. Give it a memorable name — *"Zapier production"*, *"Reporting service"*, *"Joe's laptop"*. The name only matters to you. 3. Pick a permission set: * **Read All** — read every resource the team has across all apps. Good for reporting / dashboards. * **Write All** — read and write everything. Use sparingly. * **Restrict All** — start from no permissions and tick exactly the scopes the integration needs. Best for least-privilege deployments. 4. Optionally set an **expiration date**. After that date, the key stops working. Leave empty for non-expiring keys. 5. Click *Create*. A success screen appears with the **raw key** — it starts with `looped_`. **Copy it now.** Once you close that screen, the key cannot be retrieved (we only store its hash). If you lose it, [revoke it](#revoking-a-key) and create a new one. Hand the key to the integration that needs it (their dashboard, your team's secrets manager, an `.env` file, whatever). ## Using a key [#using-a-key] Send the key in the `X-API-Key` header on every request to `https://api.looped.sh`: ```bash curl https://api.looped.sh/v1/invoices \ -H "X-API-Key: looped_..." ``` The key carries the team scope — you don't pass `teamId` separately. The full endpoint reference lives at [api.looped.sh](https://api.looped.sh) (interactive Scalar UI) or [api.looped.sh/openapi.json](https://api.looped.sh/openapi.json) (machine-readable). ## Editing a key [#editing-a-key] To rename a key or adjust its permissions / expiration: 1. Click the **⋯** menu on the key's row → *View Details*. 2. Click *Edit*. 3. Change name / scopes / expiration. 4. *Save Changes*. The key value itself does not change — your integration keeps working with the same `looped_...` value. ## Revoking a key [#revoking-a-key] Revoking immediately invalidates the key but keeps the row around so you can audit which key did what later. Use this when a key is leaked or rotated. 1. Click the **⋯** menu → *Revoke Key*. 2. Confirm. The badge flips from *Active* to *Revoked* and any further calls return `401`. ## Deleting a key [#deleting-a-key] Deleting removes the row entirely. Use this for keys created in error or that you don't need to keep on record. 1. Click the **⋯** menu → *Delete Key*. 2. Confirm. The key disappears from the table. ## Status badges [#status-badges] | Badge | Means | | ----------- | -------------------------------------------------------------------------------------------------------------------- | | **Active** | Authenticates successfully | | **Expired** | Past its expiration date. Edit it via *View Details* to set a new expiration if you want to reactivate, or delete it | | **Revoked** | Manually revoked. Cannot be reactivated — create a new key | ## Last-used timestamp [#last-used-timestamp] Each key shows a "last used" relative timestamp on the table. Useful for spotting keys that haven't run in months — candidates for cleanup. ## Best practices [#best-practices] * Give each integration its own key. Don't share a single key across services. Easier to rotate and audit. * Use **Restrict All** with explicit scopes for production keys. **Write All** is for development / personal use. * Set **expirations** on keys you'll only need temporarily (e.g. a one-off migration script). * Rotate keys periodically — every 90 / 180 days is a sensible default for production integrations. * If you suspect a leak, revoke first, ask questions later. # Billing details (/applications/manage/billing-details) Once your team has an active subscription, the **Billing** page is where you manage every aspect of it. Open it from the header — OWNER/ADMIN only. ## Page layout [#page-layout] The billing page has two states: **Free** (no active subscription): * Plan picker (*Looped One* vs *Single App*) * Seat selector * *Start Subscription* button → Stripe Checkout **Active** (subscription running): * **Current Plans** card showing each app you're paying for, with seat counts * *Manage Subscription* button → Stripe Customer Portal ## Changing the seat count [#changing-the-seat-count] You can grow or shrink your subscription without leaving Looped: 1. From the **Current Plans** card, click *Update Seats*. 2. Adjust the count. 3. Confirm. Stripe prorates immediately — adding seats charges a partial amount now, removing seats credits future invoices. The seat count must always cover your current member count. Looped will refuse to lower it past that. To shrink: [remove members first](/applications/manage/member-management#removing-a-member), then update seats. :::note Inviting a new member when you're full **automatically** grows the seat count. So most teams never have to manually bump it. ::: ## Switching between Looped One and Single App [#switching-between-looped-one-and-single-app] Plan switches happen through the **Stripe Customer Portal**. Click *Manage Subscription* on the billing page → you're redirected to Stripe. In the portal: * Switch between *Looped One* and *Single App* * Add or remove specific apps from a multi-app subscription * Update payment methods * Change billing cycle (monthly / yearly where applicable) Upgrades take effect immediately, prorated. Downgrades take effect at the **end of your current billing period**. ## Cancelling [#cancelling] Also through the customer portal: 1. Click *Manage Subscription* on the billing page. 2. Choose *Cancel subscription*. 3. Most plans default to *cancel at period end* — confirm and you're done. After the period ends, your team returns to the **Free plan**. You'll lose access to invite new members and to use the apps, but **your data stays intact** — invoices, time entries, clients. Re-subscribe at any time to reactivate. :::caution You can't [delete a team](/applications/manage/danger-zone#deleting-a-team) while a subscription is active. Cancel first. ::: ## Past invoices and receipts [#past-invoices-and-receipts] The customer portal lists every invoice from your subscription, with a download link for each PDF receipt. Useful at end of financial year. Click *Manage Subscription* → *Billing history*. ## Failed payments [#failed-payments] If a charge fails (expired card, insufficient funds), Stripe retries automatically and your subscription enters `past_due`. The dashboard shows a banner. Open the customer portal, update your payment method, and Stripe retries. If retries fail for too long, Stripe cancels the subscription and your team falls back to Free. ## Why the customer portal instead of an in-app form [#why-the-customer-portal-instead-of-an-in-app-form] Stripe's portal handles the regulatory complexity (3-D Secure for re-auth, regional taxes, mandates, refunds). We trust Stripe to do that better than we'd do it ourselves, and the portal is exactly the same one Stripe shows on its own platform — no surprises. # Danger zone (/applications/manage/danger-zone) The **Danger Zone** is the page that hides two destructive actions: **leaving** a team you're a member of, and **deleting** a team entirely. Both are irreversible. Open it from *Settings* → *Danger Zone* in the team-management nav. ## Leaving a team [#leaving-a-team] Leaving immediately revokes your access. You'll need to be re-invited to rejoin. 1. Click *Leave* in the **Leave Team** card. 2. Confirm in the modal. You're redirected to one of your other teams (or to a "no teams" landing page if this was your only team — see below). ### When you can't leave [#when-you-cant-leave] * **You're the only OWNER.** The platform shows a *"You are the only owner of this team"* alert and replaces the leave button with a link to *Manage Members*. Either promote another teammate to OWNER first (and then leave), or [delete the team](#deleting-a-team) if it's no longer needed. * **It's your last team.** Looped requires every account to belong to at least one team. Create another team first. If you're leaving because you no longer want any access at all (e.g. ending a contract relationship), leaving is the right choice — your work stays in the team and the remaining members keep using it. ## Deleting a team [#deleting-a-team] Deleting removes the team and **all of its data, permanently**. Invoices, time entries, clients, projects, members, settings — gone. There is no undo. This is OWNER-only. 1. Click *Delete* in the **Delete Team** card. 2. Type `delete my team` exactly into the confirmation input. 3. Click *Delete Team*. ### When you can't delete [#when-you-cant-delete] * **Active subscription.** Cancel first via [Manage Subscription](/applications/manage/billing-details#cancelling) → wait until the period ends and the team returns to Free. * **Last team.** Same rule as leaving — create another team first. * **Not OWNER.** ADMINs and MEMBERs see an info card on the danger zone page instead of the action button. After delete, all team members lose access immediately. You're returned to the team picker, where another team becomes your current one. ## Use cases [#use-cases] * **Leave**: you've finished a contract / left a company. The team continues without you. * **Delete**: the business is shutting down, or the team was a test you never used. Everything goes. * **Transfer ownership**: don't delete + recreate — promote someone else to OWNER, then either demote yourself to ADMIN/MEMBER or leave. ## Recovery [#recovery] If you delete a team by mistake there's no recovery path on the user side. Contact [support@looped.sh](mailto:support@looped.sh) and we'll see what's possible — recent deletes can sometimes be restored from backups, but it's not guaranteed. # Managing members (/applications/manage/member-management) The **Members** page is where OWNERs and ADMINs administer who has access to a team. Open it from the header on [app.looped.sh](https://app.looped.sh). The page has two tabs: **Members** (people who've accepted) and **Invites** (sent but not yet accepted). ## Members tab [#members-tab] Each row shows a teammate, their role, when they joined, and (if you're allowed) edit / remove buttons. ### Removing a member [#removing-a-member] Removing revokes the teammate's access immediately. Their seat does **not** auto-decrement on your subscription — adjust it yourself if you want to pay for fewer seats. 1. On the row, click the **remove** (trash) icon. 2. Confirm in the modal. Restrictions: * You can't remove yourself — use [Leave team](/applications/manage/danger-zone#leaving-a-team) instead. * Only OWNERs can remove other OWNERs. * The platform refuses to remove the last OWNER (someone has to own the team). ### Changing a role [#changing-a-role] 1. Click the **edit** (pencil) icon. 2. Pick a new role. 3. Click *Update Role*. The full permission matrix and rules are on [Roles & permissions](/applications/manage/roles). If a role is missing from the dropdown, your own role doesn't permit that change. ## Invites tab [#invites-tab] Pending invites sit here until the recipient accepts (or 7 days pass and they expire). | Column | What it tells you | | ------- | ------------------------------- | | Email | Where the invite was sent | | Role | What the recipient will join as | | Invited | Date you sent it | | Expires | 7 days after invited | Two actions per row: ### Copy link [#copy-link] Pastes the accept URL onto your clipboard. Useful when the invite email got eaten by spam — share via Slack/WhatsApp/etc. ### Delete [#delete] Cancels the pending invite. The recipient sees an *"invite is no longer valid"* page if they try to accept after that. For inviting *new* people, use the **Invite Member** form on the same page. The mechanics are covered in [Inviting team members](/applications/inviting-team-members). ## What happens to a member's data when they leave / are removed [#what-happens-to-a-members-data-when-they-leave--are-removed] Their work *stays in the team*. Time entries they logged, invoices they sent, projects they were on — all of it remains. Only their *access* is revoked. If you re-invite someone you previously removed, they get a fresh invite and rejoin as a new team membership; their previous role isn't restored automatically — set it correctly when you invite. ## Subscription seats [#subscription-seats] Adding members consumes seats. Removing them doesn't free seats automatically (so you don't accidentally overcharge yourself for a brief moment of being below the floor). To pay for fewer seats after removing someone, [lower the seat count](/applications/manage/billing-details#changing-the-seat-count) on the billing page. Stripe credits the difference to your next invoice. # Roles & permissions (/applications/manage/roles) Every member of a team has a role. Roles are fixed: OWNER, ADMIN, MEMBER. They control who can manage billing, invite teammates, change settings, and trigger destructive actions. ## The matrix [#the-matrix] | Action | OWNER | ADMIN | MEMBER | | -------------------------------------------------- | :----------------: | :---: | :----: | | Use Track / Invoices | ✅ | ✅ | ✅ | | Invite a member | ✅ | ✅ | ❌ | | Cancel a pending invite | ✅ | ✅ | ❌ | | Remove a MEMBER or ADMIN | ✅ | ✅ | ❌ | | Remove an OWNER | ✅ | ❌ | ❌ | | Promote MEMBER → ADMIN | ✅ | ✅ | ❌ | | Promote → OWNER | ✅ | ❌ | ❌ | | Demote OWNER → ADMIN | ✅ (not last OWNER) | ❌ | ❌ | | Manage billing & seats | ✅ | ✅ | ❌ | | Cancel subscription | ✅ | ✅ | ❌ | | Edit team settings (name, currency, address, bank) | ✅ | ❌ | ❌ | | Create / revoke API keys | ✅ | ✅ | ❌ | | Leave the team | ✅ (not last OWNER) | ✅ | ✅ | | Delete the team | ✅ | ❌ | ❌ | A few things this matrix is enforcing: * **A team always has ≥ 1 OWNER.** The platform refuses to demote the last OWNER or let them leave. * **You can't change your own role.** Ask a teammate. * **ADMINs can't touch OWNERs.** They can manage day-to-day operations but not the people who own the team. ## Picking a role to invite at [#picking-a-role-to-invite-at] When you [send an invite](/applications/inviting-team-members), you pick the recipient's role. A few defaults that work well: * **MEMBER** — most teammates. They can use the apps, log time, send invoices, but not change billing or kick people out. * **ADMIN** — your bookkeeper or operations lead. Same powers as you minus deleting the team and minus modifying OWNERs. * **OWNER** — co-founders, business partners. Hand this out sparingly. You can always change someone's role later from the **Members** page. ## Changing someone's role [#changing-someones-role] 1. Open *Members* in the header. 2. Click the **edit** (pencil) icon on their row. 3. Pick a new role and click *Update Role*. If a role is grayed out in the dropdown, your own role doesn't permit that change — see the matrix above. The change is effective immediately on the server. The affected teammate sees their new permissions next time they reload. ## Reading what a role grants [#reading-what-a-role-grants] Need a one-liner explanation for somebody? * OWNER: *"Can do anything in this team, including delete it."* * ADMIN: *"Runs day-to-day for the team — billing, members, integrations — but can't delete the team or touch other OWNERs."* * MEMBER: *"Uses the apps. Doesn't see billing or member admin."* # Looped Track (/applications/track) Looped Track is the platform's time-tracking app. Open it at [app.looped.sh/track](https://app.looped.sh/track). You'll do most of your tracking from the **time-logger** on the home page — quick task name, hours, project, done. Once entries pile up, [reports](/applications/track/reports) summarize them however you need (date range, project, client). [Calendar integrations](/applications/track/integrations/calendars) and [Todoist](/applications/track/integrations/todoist) let you pre-fill entries from places you're already keeping track. ## Logging time [#logging-time] The form on the home page takes: | Field | Format | Notes | | --------- | -------- | ------------------------------------------------------------------ | | Task name | text | What you worked on. Required. | | Duration | hours | Stored internally as seconds; UI shows hours (e.g. `1.5` = 90 min) | | Project | dropdown | Optional. See [Projects](/applications/track/projects) | | Date | picker | Defaults to the current Friday (week-ending date) | Type the task name → press Tab → type the duration → press *Log Time* (or hit Enter). The new entry appears in the list below, grouped by date. Entries from earlier in the week show in the same list — adding back-dated time is just changing the date field. ## Editing & deleting [#editing--deleting] Click any entry to open it in a modal: * Rename the task * Adjust duration * Move it to a different date * Reassign to a different project * Add tags (see [Tags](/applications/track/tags)) Save. Or trash-icon to delete. ## Weekly view [#weekly-view] The home page is fundamentally a weekly view. The **Weekly Total** badge in the corner shows your hours for the visible week. Scroll back/forward through dates to navigate weeks. If you'd rather slice differently, build the view in [Reports](/applications/track/reports). ## Roles in Track [#roles-in-track] * **OWNERs and ADMINs** see every member's time entries in reports and across the team. * **MEMBERs** see only their own. The reports filter for "team member" is locked to themselves. This applies in the home view too — you only see your own entries on `/track`. To see what others are tracking, use [Reports](/applications/track/reports). ## Where the data goes [#where-the-data-goes] Time entries belong to your team. They're available to: * The Track app's home view + reports * The Invoices app's [Import from Track](/applications/invoices/sending-invoices) flow, which converts time entries to invoice line items at your project rates * The [Public API](/applications/api) at `api.looped.sh/v1/track/time` ## Next steps [#next-steps] * [Set up projects](/applications/track/projects) to group your entries * [Connect a calendar](/applications/track/integrations/calendars) for one-click imports * Once you have a few weeks of data, explore [Reports](/applications/track/reports) # Projects (/applications/track/projects) Projects are how Track turns a stream of time entries into something useful — billable hours per client, hours per deliverable, who's worked on what. Open the Projects page at [app.looped.sh/track/projects](https://app.looped.sh/track/projects). ## Anatomy of a project [#anatomy-of-a-project] | Field | Notes | | ------------ | --------------------------------------------------------------------------------------------- | | **Title** | What it's called. Required. | | **Code** | Optional short identifier (e.g. `ACME-2026`). Unique per team. Useful for reports / invoices. | | **Status** | One of: `PENDING`, `ACTIVE`, `COMPLETED`, `ARCHIVED` | | **Sections** | Sub-groupings within the project (e.g. *"Discovery"*, *"Implementation"*) | | **Members** | Who has access, plus their **rate** for billing | ## Statuses, in practice [#statuses-in-practice] * **ACTIVE** — appears in the project picker on the time-logger. Default. * **PENDING** — useful for projects you're scoping but haven't kicked off yet. * **COMPLETED** — a soft "finished" state. * **ARCHIVED** — hidden from the project picker so you can't accidentally log time against it. Historical entries still show up in reports. Filter the projects list by status using the dropdown at the top. ## Creating a project [#creating-a-project] 1. Open *Projects* in the Track navigation. 2. Click *Add Project*. 3. Enter a title and (optionally) a code. 4. Pick a status — usually `ACTIVE`. 5. Save. The project is immediately available in the time-logger. ## Members and rates [#members-and-rates] Each project has its own member list. By default OWNERs and ADMINs see every project; **to give a MEMBER access to a project, add them explicitly**. Open the project's detail page → *Members* tab. | Field per member | What it does | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Role** | `MEMBER` / `MANAGER` / `OWNER` *of the project*. (Distinct from team role.) | | **Rate** | Billing rate (per hour) used by the [Import from Track](/applications/invoices/sending-invoices) flow when this user's time entries become invoice line items | | **Cost** | Internal cost (per hour) for profitability reporting | If you change someone's rate mid-project, only **future** time entries pick up the new rate. Past entries keep the rate they had when they were imported into an invoice. ## Sections [#sections] Sections are sub-folders within a project. They're optional — most teams ignore them. They're useful if a project is long-running and you want to break it down (Discovery / Build / Maintenance). Open the project detail page → *Sections* tab → *Add section*. Time entries can be assigned to a section in the same edit modal where you assign them to a project. ## Archiving vs. deleting [#archiving-vs-deleting] * **Archive** = hide from pickers, keep historical entries. Use this 99% of the time. * **Delete** = remove the project record entirely. Looped will refuse to delete a project that has time entries — you have to clear or reassign them first. ## Reporting on projects [#reporting-on-projects] [Reports](/applications/track/reports) can filter by project. The CSV export includes the project title and code per row. If you give projects clear codes, downstream finance/admin tools can join on them. # Reports (/applications/track/reports) Reports turn raw time entries into something you can hand to a client, finance team, or auditor. Two pages live under reports: * **[/track/reports](https://app.looped.sh/track/reports)** — the simple report view. Pick filters, see results, export. * **[/track/reports/builder](https://app.looped.sh/track/reports/builder)** — the report *builder*. Define a saved report you can re-run later or share with the team. ## Quick reports — `/track/reports` [#quick-reports--trackreports] Filters live on the left side; results update as you change them. | Filter | Notes | | --------------------------- | ---------------------------------------------------------------- | | **Date range** | Start + end. Defaults to the current week ending Friday. | | **Project** | Multi-select. Empty = all projects. | | **Team member** | OWNERs/ADMINs can pick anyone. MEMBERs are locked to themselves. | | **Tags** | Multi-select on time-entry tags | | **Billable / Non-billable** | Filter by the billable flag | Results show: * Total hours * Breakdown by project (and by member if applicable) * Breakdown by date * The underlying entries There's no *Run* button — change a filter and the result re-renders. ### CSV export [#csv-export] Click *Export CSV* in the corner of the report. The file contains: ``` Date, Project, Task, Member, Duration (hours), Billable ``` The export honors the filters you've applied — the CSV only contains the rows currently on screen. ## Saved reports — `/track/reports/builder` [#saved-reports--trackreportsbuilder] Where the simple page is good for one-off slices, the builder lets you **save** a configuration to re-run later. Useful for: * Weekly client status reports (filter by client's project, by week) * Monthly billable-hours summaries * Per-member productivity reviews Workflow: 1. Open [Builder](https://app.looped.sh/track/reports/builder) → click *New Report*. 2. Pick a name and description. 3. Configure filters and grouping (same as quick reports, plus pivot options). 4. Save. The saved report appears in the reports list at `/track/reports`. Anyone in the team with permission can run it. ### Permissions on saved reports [#permissions-on-saved-reports] Each saved report has two access controls: * **Allowed roles** — which team roles can view it (default: all) * **Allowed users** — explicit user ids if you want to scope it tighter than role Use this when a report contains sensitive data (e.g. partner-only profitability) and you don't want every MEMBER to see it. ## Roles & visibility [#roles--visibility] A MEMBER running a quick report only sees **their own** time entries — even if they pick a different name in the team-member filter, the API enforces own-data only. OWNERs and ADMINs see everything across the team. This applies to CSV exports too: a MEMBER can only export their own data. ## What reports don't do (yet) [#what-reports-dont-do-yet] * Send a scheduled report via email — not built. * Show $ amounts (only hours). Profit/billing summaries live in [Invoices](/applications/invoices/getting-started) once you've imported the time as line items. * Compare periods (this week vs last week). On the roadmap. If any of those would help, ping us on [Discord](https://discord.gg/euwUfRt3nt). # Tags (/applications/track/tags) Tags are short labels you can attach to time entries to slice reports beyond just project and member. Common uses: * *"Billable"* / *"Non-billable"* (though Track has a native billable flag, some teams prefer a tag) * *"Internal"* / *"Client"* * A specific phase: *"Discovery"*, *"QA"*, *"Maintenance"* * A person or pair: *"Pair w/ Alex"* A time entry can have any number of tags. ## Managing the tag list [#managing-the-tag-list] Open *Settings* → *Tags* in the Track navigation. The tags page lists your team's tags. Add new ones, rename, delete. Tags are team-scoped — every member sees the same list. ## Tagging an entry [#tagging-an-entry] Open an entry's edit modal. The **Tags** field is a multi-select with autocomplete. Pick existing tags, or type a new one to create it on the fly. ## Filtering reports by tag [#filtering-reports-by-tag] [Reports](/applications/track/reports) accept a tags filter — multi-select. Useful for billable-vs-internal slicing, phase-based summaries, etc. ## Naming conventions [#naming-conventions] Tags are most useful when they're consistent. A few tips: * **Lowercase** is easier to type and stays uniform. * **Avoid abbreviations** unless your team agrees on them. * **Don't double up with projects** — if you'd find yourself tagging every entry with the same value as the project, just use the project field. ## Cleaning up [#cleaning-up] When a tag stops being useful, delete it from *Settings* → *Tags*. Time entries that referenced it lose the tag, but the entries themselves are untouched. # Google Calendar (/applications/track/integrations/google-calendar) The Google Calendar integration imports events from your **primary** Google Calendar into Track as time entries. Useful when your meetings are already booked and you don't want to re-type them. The connection is **per user** — each teammate connects their own Google account; nobody else on the team sees your events. ## Connecting [#connecting] 1. Open *Settings* → *Integrations* in the Track navigation. 2. Click *Connect* next to **Google Calendar**. 3. Sign in with your Google account. 4. Grant Track **read-only** access on the OAuth consent screen. 5. You'll be redirected back to Looped, with the integration showing as *Connected*. Track stores OAuth tokens server-side and refreshes them automatically. To revoke, click *Disconnect* on the same page (or revoke at [myaccount.google.com/permissions](https://myaccount.google.com/permissions)). ## Importing events [#importing-events] The time-logger gets an *Import from Calendar* button once you're connected: 1. Pick a date range. 2. Track lists events in that range that you haven't already imported. 3. Tick the events you want. Edit task name or assign a project before confirming. 4. Click *Import*. Each ticked event becomes a time entry: | Calendar event field | → Time entry field | | ---------------------- | ------------------ | | Title | Task name | | Start time | Date | | Duration (end − start) | Hours | | Calendar | (not used) | | Attendees | (not used) | ## What's not imported [#whats-not-imported] * **All-day events** — no length to convert into a duration * **Declined invites** — events you didn't accept don't show up * **Events on calendars other than your primary** — only the primary calendar is read ## Deduplication [#deduplication] Track records `(connection, providerEventId)` for every imported event. Re-running the import doesn't duplicate entries you already pulled in. If you delete an imported time entry, the dedup record is removed too — so you can re-import that event later if you change your mind. ## Permissions [#permissions] Track requests **read-only** scope on your calendar (`calendar.readonly`). It cannot create, modify, or delete events. If you change your mind, disconnect from *Settings* → *Integrations* in Looped, or revoke from [myaccount.google.com/permissions](https://myaccount.google.com/permissions). ## Troubleshooting [#troubleshooting] * **"Authorization expired"** — the OAuth refresh token rotated. Disconnect and reconnect. * **No events show up** — confirm the date range is right and that you're using the same Google account that you connected. * **Stale events after I deleted them in Google** — Track caches the event list briefly. Close and reopen the import dialog to refresh. * **Sign-in fails for an enterprise tenant** — your IT team may have blocked third-party app access by policy. They'll need to permit Looped on the admin console. # Microsoft / Outlook Calendar (/applications/track/integrations/microsoft-calendar) The Microsoft Calendar integration imports events from your **primary** Outlook calendar into Track as time entries. Works for personal Microsoft accounts and Microsoft 365 (work / school) accounts alike, subject to your tenant's policy. Like the [Google Calendar integration](/applications/track/integrations/google-calendar), this is **per user** — each teammate connects their own account. ## Connecting [#connecting] 1. Open *Settings* → *Integrations* in Track. 2. Click *Connect* next to **Microsoft Calendar**. 3. Sign in at the Microsoft prompt. 4. Grant Track **read-only** access. 5. You'll land back in Looped with the integration showing *Connected*. Track stores tokens server-side and refreshes them automatically. To revoke, click *Disconnect* in Looped, or revoke from [account.microsoft.com](https://account.microsoft.com) → *Privacy* → *App access*. ## Importing events [#importing-events] The time-logger gets an *Import from Calendar* button once you're connected: 1. Pick a date range. 2. Track lists events in that range you haven't already imported. 3. Tick the events you want. Edit task name or assign a project before confirming. 4. *Import*. Each ticked event becomes a time entry — title becomes task name, length becomes the hours, the start time becomes the date. ## What's not imported [#whats-not-imported] * **All-day events** — no length to convert * **Declined invites** — only events you've accepted appear * **Calendars other than your primary** — only the primary calendar is read today ## Deduplication [#deduplication] Track records `(connection, providerEventId)` for every imported event, so re-running the import on the same range doesn't double up. Deleting an imported time entry also removes the dedup record, so you can re-import the event later if you change your mind. ## Permissions [#permissions] Track requests **read-only** scope (`Calendars.Read`). It cannot create, edit, or delete events. ## Enterprise tenants [#enterprise-tenants] If your organization uses Microsoft 365 with strict tenant policies, your IT admin may need to permit Looped explicitly before the OAuth consent screen will work. Symptoms: * The consent screen says *"Need admin approval"*. * Sign-in completes but no events ever load. In both cases, ask your admin to grant tenant-wide delegated consent for the Looped app from the [Microsoft Entra admin center](https://entra.microsoft.com). If your admin uses a direct admin-consent URL, it should use your tenant ID or `organizations`, not `common`: ```text https://login.microsoftonline.com/{tenant}/v2.0/adminconsent?client_id={client_id}&scope=https://graph.microsoft.com/User.Read%20https://graph.microsoft.com/Calendars.Read%20offline_access&redirect_uri={redirect_uri} ``` After consent is granted, each teammate still connects their own Microsoft account in Track, but non-admin users should not be asked to request permission again for the granted scopes. ## Troubleshooting [#troubleshooting] * **"Authorization expired"** — refresh token rotated. Disconnect and reconnect. * **No events show up** — confirm date range is right; confirm the connected account actually has events (it might be the wrong Microsoft account if you have multiple). * **Sign-in error mentioning the tenant** — see *Enterprise tenants* above. # Todoist integration (/applications/track/integrations/todoist) If you live in Todoist, the Todoist integration imports your **completed** tasks as time entries — so the things you've finished show up in your Track week without re-typing them. Like the [calendar integrations](/applications/track/integrations/calendars), this is **per user** — each teammate connects their own Todoist account. ## Connecting [#connecting] 1. Open *Settings* → *Integrations* in Track. 2. Click *Connect* next to **Todoist**. 3. Sign in with Todoist and grant Track read access. Track stores the OAuth tokens server-side and refreshes them automatically. Disconnect from the same page when you no longer need the integration. ## Importing tasks [#importing-tasks] Once connected, the time-logger gets an *Import from Todoist* option: 1. Pick a date range. 2. Track lists Todoist tasks **completed** in that range. 3. Tick the ones you want. Optionally adjust the duration (Todoist has no native duration concept, so each task imports with a sensible default that you should review). 4. *Import*. Each ticked task becomes a time entry — task name from Todoist, date from the completion timestamp, duration from your input. ## Limitations [#limitations] * Only **completed** tasks are pulled. Open tasks aren't. * **No project mapping** — Todoist projects don't auto-map to Track projects. Assign the project manually after import (or before, on the import screen). * **No duration carry-over** — Todoist tasks don't store hours. You enter duration at import time. * **No two-way sync** — closing a task in Todoist doesn't change anything in Track and vice versa. The integration is one-way: Todoist → Track. ## Permissions [#permissions] Track only requests **read** scope. It cannot create, modify, or delete tasks in your Todoist account. To revoke: disconnect in Looped, or remove the app from your [Todoist integrations page](https://app.todoist.com/app/settings/integrations). # Getting started with Invoices (/applications/invoices/getting-started) Looped Invoices is the platform's invoicing app. Open it at [app.looped.sh/invoices](https://app.looped.sh/invoices). You'll spend most of your time on the **invoice composer** at `/invoices/manage` — a split-pane editor with a live preview on the right. From there you can save drafts, send to clients, and accept payment online. ## Before your first invoice [#before-your-first-invoice] You need a few things in place before sending an invoice: | What | Where to set it | | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Team name + address** | *Settings* → *General* ([Team settings](/applications/team-settings)) | | **Logo** | *Settings* → *General* | | **Bank details** *(optional, for EFT)* | *Settings* → *General* | | **Currency** | Set on team creation; locks after first paid invoice | | **At least one client with an email** | [Clients](/applications/invoices/clients) | | **Payment integration** *(optional)* | *Settings* → *Integrations* → [Stripe](/applications/invoices/integrations/stripe), [Yoco](/applications/guides/yoco-integration), [PayFast](/applications/guides/payfast-integration) | The fastest way to get the team details right is the [Quick Start wizard](/applications/team-settings#quick-start), which extracts most of them from your existing website or a previous invoice PDF. ## What an invoice looks like [#what-an-invoice-looks-like] Every invoice has: * A **serial number** — auto-generated as `INV-{YEAR}-{00001}`. Resets each January. One sequence per team. * An **issue date** and optional **due date**. * A **client** (the "billed to") — picked from your client list. * A **list of line items** — description, quantity, rate, amount. * An optional **tax/VAT percentage** applied to the subtotal. * Optional **bank details** at the bottom for EFT payment. * A **status** — `PENDING` → `PAID` (with edge cases like `OVERDUE`, `EXPIRED`, `PAID_PARTIAL`). ## The flow [#the-flow] 1. **[Set up clients](/applications/invoices/clients)** — at minimum a name + email per client. 2. **[Compose an invoice](/applications/invoices/sending-invoices)** — pick the client, add line items, set tax. Save. 3. **Send it** — click the send icon. Looped emails the client a PDF and a link to a [Pay portal](/applications/pay) where they can pay online. 4. **Get paid** — once they pay (via card / EFT / etc.), the invoice is marked `PAID`. ## Importing time from Track [#importing-time-from-track] If your team logs hours in [Looped Track](/applications/track), the composer has an *Import from Track* button. Pick a date range and Track's time entries become invoice line items, with the per-project rate already applied. See [Sending invoices](/applications/invoices/sending-invoices#importing-time-from-track). ## Where to go next [#where-to-go-next] * **[Creating an account](/applications/invoices/creating-an-account)** — if you haven't yet * **[Completing your profile](/applications/invoices/completing-your-profile)** — fill in business details * **[Clients](/applications/invoices/clients)** — manage who you bill * **[Sending invoices](/applications/invoices/sending-invoices)** — the day-to-day flow * **[Stripe integration](/applications/invoices/integrations/stripe)** — accept card payments online # Clients (/applications/invoices/clients) Clients are the "billed to" side of every invoice. Before you can send an invoice, you need at least one client with an email address. The clients page is at [app.looped.sh/invoices/clients](https://app.looped.sh/invoices/clients). ## Anatomy of a client [#anatomy-of-a-client] | Field | Required? | Used for | | -------------- | --------- | ----------------------------------------------------------- | | **Name** | ✅ | "Billed to" on invoices. Unique per team (case-insensitive) | | **Email** | ✅ | Sending invoices via email; required for the "send" action | | **Phone** | optional | Surfaces on invoice for contact | | **VAT number** | optional | Surfaces on invoice in regions where it's required | | **Address** | ✅ | "Billed to" address — line 1 + postal code minimum | A client can be a person (*Jane Smith*) or a business (*Acme Corp*). Looped doesn't distinguish. ## Creating a client [#creating-a-client] Two ways: ### From the clients page [#from-the-clients-page] 1. Open *Clients* in the Invoices nav. 2. Click *Add Client*. 3. Fill the form and *Save*. ### Inline from the invoice composer [#inline-from-the-invoice-composer] When you're composing an invoice and the client doesn't exist yet: 1. Open the **Billed to** dropdown on the invoice. 2. Click **+ Add Client**. 3. Quick-create the client without leaving the composer. The client is saved, becomes the invoice's "billed to", and shows up on the clients page going forward. ## Editing a client [#editing-a-client] Open the client row → *Edit* (or just click on the client). Change anything except the name (which stays unique per team). Changes affect **future** invoices only. An invoice already sent retains the client snapshot it had at send time. ## Deleting a client [#deleting-a-client] If a client has no invoices, *Delete* removes the row. If they have invoices, deletion is blocked — you'll see a warning. Either: * Keep the client around (it doesn't cost anything), or * Delete or void the invoices first, then delete the client This safeguard prevents orphaning invoices that reference a client that no longer exists. ## Searching [#searching] The clients page has a search bar that matches name, email, phone, and VAT number. Useful when you have a long list and just want to grab someone fast. ## Why does each client need an email? [#why-does-each-client-need-an-email] The send action needs somewhere to send the email to. The send button on an invoice is **disabled** if its client has no email — the tooltip explains why. If you only ever invoice in person and don't need email sends, that's fine — leave the email blank, save the invoice, and download the PDF manually. But the bulk of the value comes from email + the [Pay portal](/applications/pay) which requires the email. ## Importing clients [#importing-clients] Bulk import isn't built yet. Until then: * Add them one at a time, or * Use the [Public API](/applications/api) (`POST /v1/invoices/clients`) to script imports from your CRM # Completing Your Profile (/applications/invoices/completing-your-profile) ## Overview [#overview] To get the best out of Looped Invoices, you'll need to complete your profile. This ensures that you create the best possible impression with your clients and generate invoices that are fully [SARS compliant](https://www.sars.gov.za/businesses-and-employers/government/tax-invoices/). A complete profile includes: * Your business or personal address * Banking details (for EFT payments) * Business information (optional) * Logo and branding (optional) ## Adding Your Address [#adding-your-address] ### Why is an address required? [#why-is-an-address-required] Your address is required to create valid, SARS-compliant invoices. According to South African tax law, all tax invoices must include the supplier's physical address. ### How to add your address [#how-to-add-your-address] 1. [Log into your Looped Invoices account](https://app.looped.sh/login) 2. Navigate to [Settings](https://app.looped.sh/settings) 3. Scroll to the **Address** section 4. Fill in the following fields: * **Street Address** - Your physical street address * **Suburb/City** - Suburb or city name * **Province/State** - Select your province * **Postal Code** - Your area's postal code * **Country** - Select South Africa (or your country) 5. Click **Update** to save your changes :::note The address you enter here will be used as the default address for all your invoices. You can override this on individual invoices if needed. ::: ### Address Tips [#address-tips] * **Use your registered business address** if you're VAT registered * **Ensure accuracy** - Your clients may need this for their records * **Keep it professional** - This appears on all your invoices * **Update when you move** - Remember to update your address if you relocate ## Adding Banking Details [#adding-banking-details] ### Why add banking details? [#why-add-banking-details] Banking details are optional but useful if you want to: * Generate PDF invoices for manual EFT payments * Provide clients with payment alternatives * Accept bank transfers alongside online payments Your banking details will appear on your invoices, making it easy for clients to pay you via electronic funds transfer (EFT). ### How to add banking details [#how-to-add-banking-details] 1. [Log into your account](https://app.looped.sh/login) 2. Navigate to the [Banking Details section of Settings](https://app.looped.sh/settings#banking-details) 3. Enter the following information: * **Bank Name** - Your bank (e.g., FNB, Standard Bank, ABSA) * **Account Holder Name** - Name on the account * **Account Number** - Your account number * **Branch Code** - Your branch code (if required) * **Account Type** - Cheque, Savings, or Business * **Reference Format** (optional) - How clients should reference payments 4. Click **Update** to save :::caution\[Security Note] Your banking details will be visible on your invoices. Never share sensitive information like your online banking passwords or PINs. ::: ### Banking Details Best Practices [#banking-details-best-practices] * **Double-check your account number** - Mistakes can delay payments * **Use a business account** if you're registered as a business * **Specify a reference format** - e.g., "Invoice number" or "Your name + Invoice #" * **Keep information current** - Update details if you change banks ## Setting Up Payment Gateways [#setting-up-payment-gateways] For automated online payments, you can connect payment gateway accounts instead of (or in addition to) adding banking details. Looped Invoices supports integration with popular South African payment gateways: ### Supported Payment Gateways [#supported-payment-gateways] * **PayFast** - [Setup guide](/applications/guides/payfast-integration) * **Yoco** - [Setup guide](/applications/guides/yoco-integration) * **More coming soon** - We're constantly adding new integrations ### Benefits of Payment Gateway Integration [#benefits-of-payment-gateway-integration] * **Instant payment notifications** - Know immediately # Creating an Account (/applications/invoices/creating-an-account) ## Overview [#overview] Creating a Looped Invoices account is quick and easy. You can sign up using your existing social media accounts, which means no need to remember another password! ## Sign Up Process [#sign-up-process] ### Step 1: Visit Looped Invoices [#step-1-visit-looped-invoices] To create an account, visit [app.looped.sh](https://app.looped.sh) and click on the **Get Started** button in the top right corner of the page. Screenshot of the login page ### Step 2: Choose Your Sign-In Method [#step-2-choose-your-sign-in-method] Looped Invoices uses secure social authentication to make account creation simple and secure. You can create an account using one of the following providers: * **Google** - Sign in with your Google account * **Apple** - Sign in with your Apple ID * **GitHub** - Sign in with your GitHub account Simply click on your preferred provider and follow the authentication prompts. ### Step 3: Grant Permissions [#step-3-grant-permissions] When you sign in with a social provider, you'll be asked to grant certain permissions: * **Basic profile information** - Your name and email address * **Profile picture** (optional) - Used for your account avatar These permissions are necessary to create and personalize your account. Looped Invoices never posts on your behalf or accesses any information beyond what's needed for authentication. ### Step 4: Account Creation Complete [#step-4-account-creation-complete] Once you've successfully authenticated, your Looped Invoices account will be created automatically. You'll be redirected to the dashboard where you can start setting up your profile. ## Security Features [#security-features] ### Social Authentication Benefits [#social-authentication-benefits] Using social authentication provides several security advantages: * **No password management** - One less password to remember or reset * **Two-factor authentication** - Leverage your provider's 2FA security * **Trusted providers** - Google, Apple, and GitHub have industry-leading security * **Easy account recovery** - Use your social provider's recovery options ### Data Privacy [#data-privacy] We take your privacy seriously: * We only access the minimum information needed (name, email, profile picture) * Your social account credentials are never stored on our servers * Authentication is handled securely through OAuth 2.0 * You can disconnect your social provider at any time ## What's Included [#whats-included] Your free Looped Invoices account includes: * Up to 5 invoices per month * Basic invoice templates * Payment link generation * Email delivery * Payment tracking * Mobile-responsive payment pages Want more? Check out our [subscription plans](/applications/manage/subscriptions) for additional features like: * Unlimited invoices * Custom branding * Payment gateway integrations * Advanced reporting * Team collaboration ## Account Setup Checklist [#account-setup-checklist] After creating your account, complete these steps to start invoicing: * [ ] [Add your business address](/applications/invoices/completing-your-profile#adding-your-address) * [ ] [Configure your banking details](/applications/invoices/completing-your-profile#adding-banking-details) * [ ] Upload your logo (optional) * [ ] [Join or create a team](/applications/manage/creating-a-team) (optional) * [ ] [Connect a payment gateway](/applications/guides/payfast-integration) (optional) * [ ] Create your first invoice! ## Multiple Accounts [#multiple-accounts] If you need to manage multiple businesses or separate personal and business invoicing: 1. Create a team for each business in [Looped Manage](/applications/manage/creating-a-team) 2. Switch between teams using the team selector in the top navigation 3. Each team can have its own branding, settings, and subscription ## Troubleshooting [#troubleshooting] ### Can't Sign In with Social Provider [#cant-sign-in-with-social-provider] If you're having trouble signing in: * **Check your pop-up blocker** - Some browsers block the authentication window * **Try a different browser** - Clear cache and cookies if issues persist * **Verify your provider account** - Ensure your social account is active and verified * **Check your email** - You may need to verify your email with your provider ### Account Already Exists [#account-already-exists] If you see a message that an account already exists: * You may have previously signed up with a different provider * Try signing in with Google, Apple, or GitHub to see which account you used * Contact support if you need to merge accounts ### Privacy Concerns [#privacy-concerns] If you prefer not to use social authentication: * Contact our support team at [support@looped.sh](mailto:support@looped.sh) * We can discuss alternative authentication options for your use case ## Next Steps [#next-steps] Now that you have an account, let's get you ready to create invoices: 1. [Complete your profile →](/applications/invoices/completing-your-profile) 2. Learn how to [create your first invoice](#) (coming soon) 3. Explore [payment gateway integrations](/applications/guides/payfast-integration) ## Need Help? [#need-help] If you encounter any issues during account creation: * 📧 Email: [support@looped.sh](mailto:support@looped.sh) * 💬 Discord: [Join our community](https://discord.gg/TwCrkdCnPS) * 🎫 Support Portal: [support.looped.sh](https://support.looped.sh) # Composing & sending invoices (/applications/invoices/sending-invoices) The invoice composer at [app.looped.sh/invoices/manage](https://app.looped.sh/invoices/manage) is where you'll spend most of your time in Invoices. It's a split-pane: * **Left** — the editor (line items, dates, client picker, tax slider) * **Right** — a live preview of the invoice as the recipient will see it Edits on the left propagate to the preview as you type. ## Composing a new invoice [#composing-a-new-invoice] 1. From the invoices list, click **New Invoice**. The composer opens with a fresh draft. 2. Fill the editor: * **Billed to** — pick a [client](/applications/invoices/clients), or create one inline. * **Issue date** — defaults to today. * **Due date** — optional. Setting one means the invoice can flip to `OVERDUE` after that date. * **Description** — an optional one-liner that appears at the top of the invoice (e.g. *"Consulting services rendered, May 2026"*). * **Line items** — see below. * **Tax / VAT** — slider, 0–100%. Applied to the subtotal. 3. Click *Save* to persist. Saving generates the **serial number** (`INV-{YEAR}-{00001}`) and assigns the next number in your team's sequence. Drafts are saved with status `PENDING` until they're paid or marked otherwise. ## Line items [#line-items] Each row of the invoice has: | Field | Notes | | --------------- | ------------------------------------------------------ | | **Description** | What you're billing for | | **Quantity** | Hours, items, units — whatever your line is in | | **Rate** | Price per unit | | **Amount** | Auto-computed (qty × rate); you can override if needed | Add lines with **+ Add Line Item**. Drag the handle to reorder. The trash icon removes a line. The subtotal, tax, and total update live in the preview as you edit. ## Importing time from Track [#importing-time-from-track] If your team uses [Looped Track](/applications/track), the composer has an **Import from Track** button: 1. Click *Import from Track*. 2. Pick a date range. 3. Track returns time entries grouped by user / project, with the **rate** for each user (set on [project membership](/applications/track/projects#members-and-rates)) already applied. 4. Tick the entries you want. Optionally edit task names or rates inline. 5. Click *Import*. Each ticked entry becomes a line item: description = task name, qty = hours, rate = project member rate. The composer's totals update. This is the fastest way to bill for time-and-materials work — log it in Track during the week, import it on Friday afternoon, send. ## Saving as a draft [#saving-as-a-draft] Click *Save*. The invoice persists with status `PENDING`. Drafts can be edited freely; they don't lock until you send. ## Sending by email [#sending-by-email] The **send** action lives on the invoice's detail panel. It only enables when the client has an email — without one, the button shows a tooltip explaining why. 1. Click the **send** icon (paper-plane). 2. Confirm the recipient + email body in the modal. 3. *Confirm*. Looped sends: * A PDF attachment of the invoice * A link to the [Pay portal](/applications/pay) where the client can pay you online (if you have any payment integrations connected) The invoice's `sentAt` timestamp is recorded; the status remains `PENDING` until payment. ## Resending [#resending] If the client says they didn't get the email, click the send icon again. The button's tooltip flips from *"Send invoice email"* to *"Resend invoice email"* once it has been sent before. ## Marking as paid manually [#marking-as-paid-manually] For payments received outside Looped (cash, EFT, etc.): 1. Open the invoice. 2. Click *Mark as paid*. 3. Pick the payment date. The invoice goes to `PAID`. If your team has [paid notifications](/applications/team-settings) enabled, an email goes out to your team. This bypasses any payment integration. To revert a mark-as-paid you'd need to ask a teammate with API access — UI undo is on the roadmap. ## Statuses [#statuses] | Status | Means | | -------------- | -------------------------------------------------------------------------------------------------------------- | | `PENDING` | Default — created but not yet paid | | `PAID` | Fully paid (manual mark or via a payment integration webhook) | | `PAID_PARTIAL` | Partial payment recorded — total received is less than the amount | | `OVERDUE` | Past due date and unpaid | | `EXPIRED` | The hosted Pay link has expired and isn't accepting new payment attempts (cosmetic — the invoice still exists) | ## Deleting an invoice [#deleting-an-invoice] You can delete any invoice from its detail page. Deletion is permanent; the line items go with it. Sequential serial numbers are not reused — there will just be a gap. # PayFast (South Africa) (/applications/invoices/integrations/payfast) PayFast is a South African payment provider. Connecting it to Looped Invoices lets your clients pay invoices via card, EFT, or any of PayFast's other supported methods (Mukuro, Zapper, etc.). The integration is **per team** — connect a different PayFast merchant account per team if you want. ## Prerequisites [#prerequisites] * A PayFast merchant account. Sign up at [registration.payfast.io](https://registration.payfast.io/) if you don't have one. * OWNER or ADMIN on the Looped team. ## Get your PayFast credentials [#get-your-payfast-credentials] 1. Log in to PayFast at [login.payfast.io](https://login.payfast.io). Pick **Aggregation** at sign-in. 2. In the sidebar, click *Settings* → *Developer Settings*. 3. Take note of three values from the top of the page: * **Merchant ID** * **Merchant Key** * **Security Passphrase** (just below) :::note We strongly recommend leaving **"require signature"** turned on in PayFast — this enables the passphrase-based signature verification that Looped uses to confirm payment authenticity. Don't disable it. ::: ## Connect PayFast in Looped [#connect-payfast-in-looped] 1. Open *Settings* → *Integrations* in Looped Invoices. 2. Click *Connect* next to **PayFast**. 3. Paste the three values from above. 4. Click *Save*. The badge flips to *Connected* and PayFast becomes a payment option on every invoice you send going forward. ## What clients see [#what-clients-see] When you send an invoice, the email and the [Pay portal](/applications/pay) show a *"Pay with PayFast"* button. Clicking it submits a form to PayFast's hosted checkout where your client picks their payment method (card, EFT, etc.) and pays. After payment: 1. PayFast posts a notification to Looped at `app.looped.sh/invoices/api/payfast/notify/`. 2. Looped verifies: * The HMAC **signature** using your passphrase * The originating IP is a known PayFast IP * A **server confirmation** call back to PayFast (defense-in-depth) 3. If all three checks pass, the invoice flips to `PAID`. 4. Funds settle into your PayFast wallet on PayFast's standard schedule. If the payment fails, the invoice stays `PENDING` and the client can retry from the same Pay link. ## Fees [#fees] Looped doesn't take a cut. Standard PayFast transaction fees apply — see [payfast.io/fees](https://www.payfast.io/fees/) for current rates. ## Disconnecting [#disconnecting] Open *Settings* → *Integrations* in Looped → click *Disconnect* next to PayFast. Past invoices already paid stay marked paid. New invoices stop offering PayFast as a method. ## Multiple payment integrations [#multiple-payment-integrations] You can have PayFast alongside Stripe, Yoco — the client picks one on the Pay portal. Many SA teams enable both PayFast and Yoco for redundancy + payment-method coverage. ## Testing the integration [#testing-the-integration] PayFast has a **sandbox** for testing without real money. To use it, set up a separate sandbox account at [sandbox.payfast.io](https://sandbox.payfast.io) and connect those credentials to a *test* Looped team. You can then send test invoices and pay them with PayFast's test cards. For full production confidence, send yourself a small live invoice and pay it for real — easy to refund afterwards from the PayFast dashboard. ## Troubleshooting [#troubleshooting] * **Payment succeeds but Looped doesn't flip to PAID** — most often signature verification failure. Check that the **Security Passphrase** in Looped exactly matches what's set in PayFast (no leading/trailing spaces, copy carefully). If it does, drop us a line at [support@looped.sh](mailto:support@looped.sh) — there may be an IP-allowlist issue. * **Pay button isn't showing** — the invoice might have been sent before you connected the integration. Re-send the invoice; the new Pay link will include PayFast. * **Sandbox payments aren't going through** — make sure the credentials in Looped are the sandbox credentials, not the production ones. The two are separate. # Stripe (accept card payments) (/applications/invoices/integrations/stripe) Connect a Stripe account to Looped Invoices and the **Pay** link in your invoice emails accepts credit / debit cards. Funds settle into your Stripe account on Stripe's normal payout schedule — Looped never touches the money. This Stripe account is **separate** from the one Looped uses for your subscription billing. They can be the same account or different ones; they aren't linked. ## Prerequisites [#prerequisites] * A Stripe account with payouts enabled. Sign up at [stripe.com](https://stripe.com) if you don't have one. * OWNER or ADMIN on the Looped team. ## Connecting [#connecting] 1. Open *Settings* → *Integrations* in Looped Invoices. 2. Click *Connect* next to **Stripe**. 3. Stripe opens. Sign in and grant Looped access. 4. Stripe redirects back to Looped. The integration's badge now shows *Connected*. Looped stores your Stripe **account id**. Your Stripe **secret key** stays on Stripe; we never see it. A webhook endpoint at `app.looped.sh/invoices/api/stripe/notify/` is registered automatically so Stripe can notify Looped when payments succeed. ## What clients see [#what-clients-see] When you send an invoice, the email and the [Pay portal](/applications/pay) include a *"Pay now"* button. Clicking it opens **Stripe Checkout** — Stripe's hosted payment page — where the client enters their card details. After they pay: 1. Stripe sends a webhook to Looped. 2. Looped marks the invoice as `PAID` with the timestamp Stripe confirmed the charge. 3. Funds settle into your Stripe account. If the payment fails, the invoice stays `PENDING` and the client can retry from the same Pay link. ## Fees [#fees] Looped doesn't take a cut of the payment. Standard Stripe processing fees apply — see [stripe.com/pricing](https://stripe.com/pricing) for your region. ## Disconnecting [#disconnecting] Open *Settings* → *Integrations* → click *Disconnect* next to Stripe. Past invoices that were already paid through Stripe stay marked paid. Disconnection only stops new invoices from offering Stripe as a payment method. If you reconnect a *different* Stripe account, future invoices route to the new account; past invoices are unchanged. ## Why it's per-team [#why-its-per-team] The Stripe connection is scoped to a Looped team — different teams in your account can have different Stripe accounts (or one of them can be Stripe-less). This makes sense if you run multiple businesses through Looped. ## Multiple payment integrations at once [#multiple-payment-integrations-at-once] You can have Stripe **and** Yoco **and** PayFast connected at the same time. The Pay portal shows whichever ones you've connected; the client picks one when paying. ## Alternatives [#alternatives] If Stripe isn't available in your region, see: * **[Yoco](/applications/guides/yoco-integration)** — South Africa * **[PayFast](/applications/guides/payfast-integration)** — South Africa ## Webhook details (for troubleshooting) [#webhook-details-for-troubleshooting] Looped registers a webhook for these Stripe events: * `checkout.session.completed` → mark the invoice paid * `payment_intent.payment_failed` → record the failure (invoice stays `PENDING`) If you ever see Stripe payments succeed but Looped not flip the invoice to `PAID`, check your Stripe dashboard's *Developers* → *Webhooks* section — the endpoint should show recent events being delivered. If something looks off, drop us a line at [support@looped.sh](mailto:support@looped.sh). # Yoco (South Africa) (/applications/invoices/integrations/yoco) Yoco is a South African payment processor. Connecting it to Looped Invoices lets clients pay you with a credit / debit card from any invoice you send. This integration is **per team** — different teams in your account can have different Yoco accounts, or only some teams can use Yoco at all. ## Prerequisites [#prerequisites] * A Yoco merchant account. Sign up at [hello.yoco.com/signup](https://hello.yoco.com/signup) if you don't have one. * OWNER or ADMIN on the Looped team. ## Get your Yoco API keys [#get-your-yoco-api-keys] 1. Log in to your Yoco account at [portal.yoco.co.za](https://portal.yoco.co.za). 2. In the left sidebar, click *Sell Online* → *Payment Gateway*. 3. Scroll to **Your keys**. 4. Take note of: * **LIVE PUBLIC KEY** (starts with `pk_live_…`) * **LIVE SECRET KEY** (starts with `sk_live_…`) You'll paste these into Looped in the next step. :::caution Treat the **secret key** the same way you'd treat a password — never paste it into a public chat, email, or shared spreadsheet. Looped stores it encrypted. ::: ## Connect Yoco in Looped [#connect-yoco-in-looped] 1. Open *Settings* → *Integrations* in Looped Invoices. 2. Click *Connect* next to **Yoco**. 3. Paste the **public** and **secret** keys you copied from Yoco. 4. Click *Save*. The badge flips to *Connected* and Yoco appears as a payment option on every invoice you send going forward. ## What clients see [#what-clients-see] When you send an invoice, the email and the [Pay portal](/applications/pay) include a *"Pay with Yoco"* button. Clicking it opens Yoco's hosted checkout where the client enters card details. After they pay: 1. Yoco posts a webhook to Looped at `app.looped.sh/invoices/api/yoco/notify/`. 2. Looped marks the invoice as `PAID`. 3. Funds settle into your Yoco account on Yoco's standard schedule. If the payment fails, the invoice stays `PENDING` and the client can retry from the same Pay link. ## Fees [#fees] Looped doesn't take a cut. Standard Yoco transaction fees apply — see [yoco.com/za/fees](https://www.yoco.com/za/fees/) for current rates. ## Disconnecting [#disconnecting] Open *Settings* → *Integrations* in Looped → click *Disconnect* next to Yoco. Past invoices that were already paid stay marked paid. Disconnection only stops new invoices from offering Yoco. If you reconnect with **different** keys later, future invoices use the new account; past invoices are unchanged. ## Multiple payment integrations [#multiple-payment-integrations] You can have Yoco connected alongside Stripe, PayFast — clients pick which to use on the Pay portal. Some teams enable just one; others offer multiple to give clients flexibility. ## Testing the integration [#testing-the-integration] The simplest end-to-end test: 1. Send yourself a small-value invoice (e.g. R10) to a personal email. 2. Open the Pay link. 3. Pay with a real card — small enough that you don't mind eating the fee. 4. Confirm the invoice flips to `PAID` in Looped within a few seconds of payment confirmation. You can refund yourself from the [Yoco portal](https://portal.yoco.co.za) afterwards. ## Troubleshooting [#troubleshooting] * **"Yoco not configured"** — check the connection in Looped settings; the keys may have been entered wrong or never saved. * **Payment succeeds in Yoco but Looped doesn't flip to PAID** — Yoco's webhook didn't reach Looped. Check Yoco's portal for webhook delivery logs and verify the webhook URL is `app.looped.sh/invoices/api/yoco/notify/`. Reach out to [support@looped.sh](mailto:support@looped.sh) if it's not. * **Pay button is missing on the Pay link** — the integration might not be enabled, or the invoice was sent before you connected. Re-send the invoice and the new Pay link will include Yoco. # Looped Pay (/applications/pay) When you send an invoice from [Looped Invoices](/applications/invoices/getting-started), the email contains a unique **pay link** that takes the recipient to Looped Pay. They don't need a Looped account; the link itself is the credential. Pay lives at `pay.looped.sh/`. Each link is tied to a single invoice. ## What the recipient sees [#what-the-recipient-sees] * **Your branding** — logo and team name from your [team settings](/applications/team-settings) * **The invoice** — line items, totals, currency * **Payment methods** — whichever payment integrations you've enabled (Stripe, Yoco, PayFast) * A **Download Invoice** button so they can save the PDF * After payment: a **"Paid"** badge replaces the payment buttons If you have no payment integrations connected and no bank details on file, the page just shows the invoice — your client falls back to whatever payment arrangement you have offline. ## The payment flow [#the-payment-flow] 1. Client clicks **Pay now**. 2. They're redirected to the chosen provider's hosted checkout (Stripe / Yoco / PayFast). 3. They enter card details and pay. 4. Provider redirects them back to the Pay page. 5. Provider sends a webhook to Looped → invoice flips to `PAID`. The Pay page polls the invoice's status when it loads, so a returning client sees the new `Paid` state immediately. ## Statuses the recipient might see [#statuses-the-recipient-might-see] | Status | What it means for them | | -------------- | ------------------------------------------------------------ | | `PENDING` | Default — payment buttons visible | | `PAID` | Done. Payment buttons hidden, "Paid" badge shown | | `PAID_PARTIAL` | Partial payment recorded — they'll see the remaining balance | | `OVERDUE` | Past due — payment buttons still work; this is cosmetic | ## Privacy [#privacy] The pay link is public — anyone with it can view and pay the invoice. There's no sign-in. The link contains a CUID2-format invoice id which is unguessable in practice, but treat it the same way you'd treat a payment slip: don't post it on social media, and if you sent it to the wrong person, **delete the invoice** ([Sending invoices](/applications/invoices/sending-invoices)) so the link stops loading. ## Refunds [#refunds] Looped does **not** issue refunds. Refunds are processed from your payment provider's dashboard: * Stripe → [dashboard.stripe.com](https://dashboard.stripe.com) → invoice → Refund * Yoco → Yoco Business portal * PayFast → PayFast merchant portal After issuing a refund, you can [mark the invoice paid manually](/applications/invoices/sending-invoices#marking-as-paid-manually) again or [delete it](/applications/invoices/sending-invoices#deleting-an-invoice) — Looped doesn't auto-revert payment status when a provider issues a refund. ## What's *not* on the Pay page [#whats-not-on-the-pay-page] * No account, no login, no order history. * No saved cards (Stripe handles this on its own checkout, not on Pay itself). * No installments / payment plans. * No support chat. (If a client has questions, they need to contact you directly.) The page is intentionally minimal — its job is to let your client pay quickly and get out of the way. # Public API (/applications/api) Looped's public API is the same surface the apps use — exposed for external integrations. It lives at **[api.looped.sh](https://api.looped.sh)**. That URL renders the **interactive Scalar reference** — every endpoint, every parameter, every response shape, with a "Try it" panel you can run live calls from. For machine consumption, the OpenAPI 3.0 spec is at **[api.looped.sh/openapi.json](https://api.looped.sh/openapi.json)**. Drop it into Postman, Bruno, or your codegen of choice. ## Authenticating [#authenticating] Every request needs an API key in the `X-API-Key` header. Generate one from your team's [API keys page](/applications/manage/api-keys), then: ```bash curl https://api.looped.sh/v1/invoices \ -H "X-API-Key: looped_..." ``` The key carries the team scope — you don't pass `teamId` separately. Each key also carries a **scope set** (read-only / write / restricted) configured when the key was created. | Status | Cause | | ------------- | -------------------------------------------------------------- | | `200` / `201` | Success | | `400` | Validation error — body explains the offending field | | `401` | Missing / invalid / expired / revoked `X-API-Key` | | `403` | Key is valid but doesn't include the scope this endpoint needs | | `404` | Resource not found (or in a different team) | ## Endpoint surface (high-level) [#endpoint-surface-high-level] | Path prefix | Resource | | ---------------------- | ------------------------------------------------------- | | `/v1/invoices` | Invoices, line items, marking statuses, generating PDFs | | `/v1/invoices/clients` | Clients (the "billed to" side of invoices) | | `/v1/track/time` | Time entries | | `/v1/track/projects` | Projects | | `/v1/track/sections` | Sections within a project | | `/v1/track/tags` | Tags on time entries | The exact list and response shapes are authoritative in [the live reference](https://api.looped.sh). ## Rate limits [#rate-limits] Requests are rate-limited per API key. If you exceed the limit you'll get a `429` with a `Retry-After` header. Most well-behaved integrations never hit it. ## Idempotency [#idempotency] Mutating endpoints (POST/PATCH/DELETE) are not idempotent by default. If you need at-most-once semantics for a workflow that might retry, dedupe on your side or use a deterministic id pattern in your application logic. ## SDKs [#sdks] We don't ship official SDKs yet. Generate one for your language from the OpenAPI spec — most popular languages have a one-shot generator (`openapi-generator`, `swagger-codegen`, etc.) that produces a usable client in a couple of minutes. ## Where to go next [#where-to-go-next] * Browse the [interactive reference](https://api.looped.sh) * [Create your first key](/applications/manage/api-keys#creating-a-key) * Read the [OpenAPI spec](https://api.looped.sh/openapi.json) # Creating valid invoices (/applications/guides/valid-invoices) In South Africa, there are two main types of invoices that are considered [valid by SARS](https://www.sars.gov.za/businesses-and-employers/government/tax-invoices/). These are full tax invoices and abridged invoices. In this guide, we will discuss the differences between these two types of invoices. ## Invoices vs Abridged Invoices [#invoices-vs-abridged-invoices] Full tax invoices are required for transactions with a value of R5000.00 or more (including VAT). Abridged invoices are permitted for transactions with a value greater than R50.00 and less than R5000.00 (including VAT). ### Full Tax Invoices [#full-tax-invoices] Full tax invoices are comprehensive documents that provide a detailed breakdown of a transaction between a buyer and a seller. These invoice are required to contain: 1. The words "Tax Invoice", "VAT Invoice" or "Invoice" 2. Name, address and VAT registration number of the supplier 3. Name, address and where the recipient is a vendor, the recipient’s VAT registration number 4. Serial number and date of issue of invoice 5. Accurate description of goods and /or services (indicating where applicable that the goods are second hand goods) 6. Quantity or volume of goods or services supplied 7. Value of the supply, the amount of tax charged and the consideration of the supply (value and the tax) ### Abridged Invoices [#abridged-invoices] Abridged invoices, on the other hand, are simplified versions of regular invoices. They are commonly used for transactions with lower values or for quick payment requests. Abridged invoices may contain less detailed information but still capture essential elements like: 1. The words "Tax Invoice", "VAT Invoice" or "Invoice" 2. Name, address and VAT registration number of the supplier 3. Serial number and date of issue of invoice 4. Accurate description of goods and /or services (indicating where applicable that the goods are second hand goods) 5. Value of the supply, the amount of tax charged and the consideration of the supply (value and the tax) ## Complying with SARS [#complying-with-sars] Understanding the distinctions between regular invoices, abridged invoices, and adhering to the SARS valid [invoice checklist](https://www.sars.gov.za/wp-content/uploads/Docs/Government/Tax-Invoice-Checklist-Version-2-29032016.pdf) is crucial for businesses to maintain transparency, ensure legal compliance, and streamline financial processes. ## We've got you covered [#weve-got-you-covered] By using invoicelink, you can rest assured that we will ensure that the invoices you produce are always valid. When you use our Quick Links feature, which is enabled for payment requests up to R5000.00, we automatically produce an abridged invoices for you. This means that the only information we need to collect from you is the description of the goods or services you are requesting payment for and the amount. For any other transactions, we allow you to generate comprehensive invoices that contain all the information required by SARS. We also allow you to customise the invoice according to your brand identity as well as turn them into templates that can be reused later. # Designing invoice templates (/applications/tutorials) Coming soon!