# FAQ (/docs/faq)
Common questions about setting up and running a store with Allomancy. Each answer is short by design; click through to the linked guide for the full walkthrough.
## Setup and Installation [#setup-and-installation]
## How do I get started with the Allomancy system? [#getting-started]
Create an account in [Allomancy Stores](https://stores.allomancy.com), link your Second Life username, and follow the inworld link Allomancy sends you. After the link succeeds, Allomancy sends your setup package inworld.
See: [Account Creation](/docs/quickstart/account-creation)
## Does my Dispenser need to be on the same land as my store? [#dispenser-placement]
Your dispenser does not need to be in the same region as your store, but at least one dispenser containing your products must stay rezzed inworld for deliveries to work.
See: [Dispenser Setup](/docs/quickstart/dispenser-setup)
## Can I use my own custom mesh or objects for vendors? [#custom-mesh-vendors]
Yes. Rez the original Allomancy vendor, drag its script into your inventory, and drop the script into your custom mesh. After you drop the script into the new object, accept the debit permission request to finish the setup.
See: [Vendor Setup](/docs/quickstart/vendor-setup)
## Can I use the system for my bloggers only? [#bloggers-only-use]
Yes. You can use the Blog Dashboard to manage applications, bloggers, product access, posts, and deadlines. Mark products **Bloggable** to make them available to bloggers.
See: [Blogger Management](/docs/features/blogger-management)
## Product and Inventory Management [#product-and-inventory-management]
## Why aren't my items appearing in the web dropdown menu when I try to list a product? [#items-not-appearing]
First confirm the item is inside a rezzed dispenser and is not already listed; the product picker shows unlisted dispenser items first. If it still does not appear, check the inworld item permissions.
See: [Product Listing Quickstart](/docs/quickstart/product-listing)
## How do I update a product for my customers? [#update-product-customers]
Two paths. To keep the same product, remove the old item from the dispenser and add the updated version with the exact same name. Alternatively, edit the product listing on the website and change **Item to be delivered** to the new item, then save. Either way, use the product's tool and choose **Mass redelivery** to send a fresh copy to customers who already own the product.
See: [Product Tools](/docs/features/product-tools)
## Can I own multiple brands or stores from one account? [#multiple-stores]
Each Allomancy account owns one store with its own name, logo, and customer-facing pages. If you want fully separate branding, create a separate account on a different Second Life avatar.
See: [Store Settings](/docs/features/store-settings)
## Can I delete old or unlisted items from the website? [#delete-products]
Yes. Select products in the Products table and click the delete button. Deleted products can no longer be redelivered from customer purchase history.
See: [Product Listing](/docs/features/product-listing)
## Vendor Functionality and Discounts [#vendor-functionality-and-discounts]
## How do I set up group-only items or group gifts? [#group-only-vendors]
In the vendor dialog, enable **Only sell for group members** for group-only sales. For free click-to-get gifts, set the vendor price to L$0 and enable **Touch to acquire when price is L$0**.
See: [Vendors](/docs/features/vendors)
## Can I apply discounts to an entire region or category? [#region-category-discounts]
Use Bulk Update when you want to change vendors by region, product name, or UUIDs. Product or category-level changes live in the product tools instead.
See: [Vendors](/docs/features/vendors)
## Why is my vendor refunding customers automatically? [#auto-refunds]
Check **Refund duplicate purchases** in Store Settings; when it is on, copy products a customer already owns are refunded automatically. Group-only vendors require the customer's active group to match.
See: [Store Settings](/docs/features/store-settings)
## How do I remove the "Choose amount" field from the payment menu? [#disable-custom-payment]
Open Vendor Settings and turn off **Custom payment field**. When it is on, customers can pay part of the price in L$ and use store credit for the rest.
See: [Vendors](/docs/features/vendors)
## Migration from Other Systems [#migration-from-other-systems]
## Can I import my sales history from my previous vending system? [#import-sales-history]
Yes. You can import sales transactions from a CSV and map each product name to an existing dispenser item. Customer data and credit balances are handled by Customer Import, not by the sales import flow.
See: [Imports & Exports](/docs/features/imports-and-exports)
## Will my product textures and settings migrate automatically? [#migrate-textures]
No. Sales CSV imports do not bring over product images, textures, or product-specific settings. After importing transactions, review your product listings and update product details separately.
See: [Imports & Exports](/docs/features/imports-and-exports)
## How do I handle existing gift card balances from my old system? [#migrate-gift-cards]
If your old system can export balances, Customer Import can bring in regular credit, gift card credit, and restricted credit from CSV columns. If you do not have exportable data, update customer credit manually from the Customers page.
See: [Customer Import](/docs/features/customer-import)
## Gift Cards and Store Credit [#gift-cards-and-store-credit]
## How do I sell gift cards? [#selling-gift-cards]
There are two paths. From the web, issue gift cards to specific customers from **Gift Cards > Customers cards**. To let customers buy gift cards themselves, rez a **Customer & Gift Card Terminal** in your mainstore; customers can then pay the amount they want loaded onto the card.
See: [Gift Cards](/docs/features/gift-cards#selling-gift-cards-inworld)
## How do I set up a free store credit giveaway? [#free-credit-giveaway]
Create a Gift Card Delivery Unit, copy its unit ID, and paste that ID into the inworld object's description. Allomancy uses that ID to pair the object with the delivery unit you created.
See: [Gift Card Delivery Units](/docs/features/gift-card-delivery-units)
## What is the difference between regular and restricted credit? [#regular-vs-restricted-credit]
Allomancy tracks regular credit and restricted credit separately. Restricted credit cannot be used for gifting, which makes it useful for giveaways where the credit should stay with the original customer. Either kind can be granted with an expiry date, and credit closest to expiring is spent first.
See: [Store Credit](/docs/features/store-credit)
## Can customers pay for an item using both credit and Lindens? [#mixed-credit-payment]
Yes, when **Custom payment field** is enabled in Vendor Settings (it is on by default). The customer pays part of the price in L$, and Allomancy uses their store credit for the missing amount. If **Custom payment field** is off, the customer can buy a gift card for the missing amount and redeem it instead.
See: [Vendors](/docs/features/vendors)
## Blogger Management [#blogger-management]
## How do bloggers retrieve their items? [#blogger-retrieve-items]
Approved bloggers log into the blogger-facing site, open a store's available products, and request delivery from the product card. Products marked **Bloggable** are the ones available for that workflow.
See: [Blogger Management](/docs/features/blogger-management)
## What happens if a blogger misses a deadline? [#blogger-missed-deadline]
Allomancy tracks product deadlines so you can see which bloggers have upcoming or overdue work. Suspension is a status you set yourself from the blogger profile when you decide it's warranted.
See: [Deadlines](/docs/features/bloggers/deadlines)
## Can I prevent a rejected blogger from applying again? [#prevent-blogger-reapplying]
Yes. Ban a blogger from the Blog Dashboard's **Bans** page to block them. The reason is only visible to the store owner and managers with the right permissions.
See: [Moderation](/docs/features/bloggers/moderation)
## Subscriptions and Pricing [#subscriptions-and-pricing]
## What are the available pricing models? [#pricing-models]
Allomancy supports weekly subscription billing or a 5% commission on each sale. A subscription removes the 5% transaction fee while it is active.
See: [Pricing](/docs/pricing)
## What happens if my subscription expires? [#subscription-expires]
Your store keeps operating normally. Allomancy automatically switches the store back to the 5% commission model until you subscribe again.
See: [Pricing](/docs/pricing)
## Are there different features for higher subscription tiers? [#higher-tier-features]
Higher plans add bot-related features, such as connecting a Corrade bot. See the Subscriptions page for the current plan cards.
See: [Subscriptions](/docs/features/subscriptions)
# Pricing (/docs/pricing)
Allomancy gives you two billing options. You can subscribe to a weekly plan to avoid per-sale commission, or you can stay on commission and only pay when a product sells.
## Plans [#plans]
For the full plan list and what each tier adds, see [Subscriptions](/docs/features/subscriptions).
## How to subscribe [#how-to-subscribe]
Open the [subscriptions page](https://stores.allomancy.com/subscriptions), pick a plan, choose how many weeks, and confirm the payment.
The subscription charge is collected through one of your rezzed vendors. You need at least one active vendor inworld for the payment to succeed.
## Automatic fallback [#automatic-fallback]
If you do not have an active subscription, or your subscription expires, Allomancy automatically switches your store to the 5% commission model. Your store keeps operating normally, and you do not need to change anything inworld.
# Privacy Policy (/docs/privacy-policy)
**Effective Date:** April 23, 2026\
**Last Updated:** April 25, 2026
This is the Allomancy Privacy Policy. We've tried to keep it short, honest, and readable. If anything is unclear, email us at **[archis@allomancy.com](mailto:archis@allomancy.com)** and we'll explain.
## 1. Who We Are [#1-who-we-are]
**Allomancy Desenvolvimento de Software LTDA**\
CNPJ: 60.078.642/0001-35\
Based in Brazil.
Allomancy is a vending and management platform for residents of the virtual world Second Life. We help merchants run stores, track sales, and manage inworld products.
> **About the name.** "Allomancy" is just our brand name. It has no connection to, affiliation with, or endorsement from any book, author, or media franchise.
**Data Protection Officer (Encarregado pelo Tratamento de Dados Pessoais):**\
Archis Conundrum: [archis@allomancy.com](mailto:archis@allomancy.com)
Archis Conundrum is a Second Life name used in a professional capacity for this role.
## 2. Some Terms Used in This Policy [#2-some-terms-used-in-this-policy]
To keep things clear, here are a few terms we use throughout this policy:
* **Merchant**: a Second Life resident who has registered an Allomancy account to run their store.
* **Buyer**: a Second Life resident who purchases from an Allomancy-powered vendor.
* **Inworld device**: a scripted object placed inside Second Life by a merchant (such as a vendor or dispenser) that is part of the Allomancy system.
* **Dashboard**: the Allomancy web application where merchants configure their stores and view their data.
## 3. What We Collect [#3-what-we-collect]
We aim to collect only what we need to provide, secure, and improve the service. The main categories of data we collect are:
### From you (our registered users) [#from-you-our-registered-users]
* **Your email address**, when you sign up through Google Firebase. We use it only to sign you in and send you essential service messages. We do not use it for advertising.
* **Your Firebase User ID**: an internal identifier from Firebase that we use to recognize your account.
* **Your Second Life Username and UUID**, so we can link your Allomancy account to your inworld identity.
* **Your store settings and preferences**: everything you configure in your dashboard.
* **API keys you generate**, stored only in hashed form using a strong one-way hash. We cannot recover the original key. If you lose it, generate a new one.
### From inworld activity (when a purchase happens) [#from-inworld-activity-when-a-purchase-happens]
When a buyer purchases something from an Allomancy-powered vendor, we record:
* The buyer's Second Life Username and UUID
* The region, position, and details of the vendor and dispenser
* Transaction details (product, price, timestamp)
We do **not** receive or store the buyer's IP address, email, or any real-world information. Second Life Usernames and UUIDs are visible to other residents within Second Life and function as public identifiers for inworld purposes. However, because they are tied to a natural person behind the avatar, we still handle them as personal data where applicable law requires, and we apply the same security and retention controls as to other personal data.
### Other data collected automatically [#other-data-collected-automatically]
* **Technical data**: basic request metadata such as browser type, user agent, and timestamps, which are generated by standard server and infrastructure logs.
* **Account activity timestamps**: such as last login time, used for security and account management.
* **Support correspondence**: if you email us or contact us for support, we keep a record of the conversation so we can help you and improve our service.
### For payments [#for-payments]
Allomancy only accepts payments in **Linden Dollars (L$)**, handled entirely inside Second Life by Linden Lab. We never see or process your credit card, bank account, or any real-world financial information.
### Automatically, through infrastructure [#automatically-through-infrastructure]
* **Audit logs**: a detailed record of user and bot actions within the system, used for security, debugging, and dispute resolution.
* **Network data via Cloudflare** (DNS, reverse proxy, DDoS protection). Cloudflare briefly processes IP addresses for security purposes.
* **Cloudflare Web Analytics**, which is privacy-focused and does **not** use cookies, fingerprinting, or cross-site tracking. See [Cloudflare's privacy page](https://www.cloudflare.com/web-analytics/) for details.
* **Firebase Authentication telemetry** that Google collects as part of providing the auth service. We don't control this; see [Firebase's privacy page](https://firebase.google.com/support/privacy).
### Cookies [#cookies]
We use **only essential cookies**:
* **Authentication cookies**: to keep you signed in.
* **Preference cookies**: to remember choices you've made in the service, such as display or customization settings.
We do not use advertising cookies, tracking pixels, third-party analytics cookies, or cross-site trackers. We use cookies only where they are necessary to provide the service, keep you signed in, secure your account, or remember choices you have made. If we add non-essential cookies in the future, we'll ask for your consent where required by law.
## 4. How We Use Your Information [#4-how-we-use-your-information]
Only for these purposes:
* To run the service: sign you in, link your Second Life account, operate your stores, and record transactions.
* To email you about essential things: account status, security alerts, billing, or material changes to this policy.
* To keep the service secure: detect fraud, abuse, and unauthorized access.
* To debug problems and improve the service.
* To comply with legal obligations.
**We do not:**
* Sell your data.
* Share it with advertisers.
* Use it to train AI or machine-learning models.
* Send you marketing emails.
If we ever add optional features (like weekly sales reports by email), we'll ask you to opt in first.
## 5. The Merchant / Buyer Distinction [#5-the-merchant--buyer-distinction]
Allomancy works with two kinds of people, and it's important to understand the difference:
### If you are a registered user (merchant) [#if-you-are-a-registered-user-merchant]
Allomancy is the **controller** of your personal data (your email, settings, etc.). You can exercise the rights described in this policy, subject to the limits and exceptions explained below.
**What other users can see:** Other Allomancy users in your store's context can see your Second Life Username and any store name, product, or store information you choose to publish. Your email address, Firebase User ID, private account settings, and API keys are **never visible to other users**.
### If you are a buyer who purchased from a merchant using Allomancy [#if-you-are-a-buyer-who-purchased-from-a-merchant-using-allomancy]
When a buyer purchases from an Allomancy-powered vendor, Allomancy acts in two different roles depending on the purpose of the processing:
* **Merchant-facing role (processor).** The merchant is the controller of their own customer and sales records. For handling those records on the merchant's behalf (such as storing, displaying, and organizing their sales history), Allomancy acts as a **processor** on the merchant's instructions.
* **Platform-wide role (independent controller).** Separately, Allomancy processes some buyer data for its own purposes, including platform security, fraud and abuse prevention, audit logging, debugging, dispute handling, legal compliance, and maintaining the integrity of the service. For these purposes, Allomancy acts as an **independent controller**, and does so based on our legitimate interests and, where applicable, legal obligations.
Your Second Life Username and UUID are stored as part of the merchant's transaction records because this is necessary for the merchant (for example, to handle refunds, deliver products, or ban abusive buyers). These identifiers also appear in Allomancy's own security and audit systems for the platform-wide purposes described above.
**If you are a buyer** and want to exercise rights over your purchase records, you can contact the merchant directly (they control their sales history) or contact us at [archis@allomancy.com](mailto:archis@allomancy.com) and we'll help route your request appropriately. We may not always be able to delete records that merchants or Allomancy need to keep for legitimate purposes, such as completing transactions, handling disputes, preventing abuse, complying with legal obligations, or defending legal claims, but we will respond to every request and explain our reasoning.
If a merchant closes their Allomancy account, their transaction data is deleted within 90 days, subject to any audit log retention described in Section 8.
## 6. Who We Share Data With [#6-who-we-share-data-with]
We share personal data only with the infrastructure providers we need to run the service. Each one processes data under terms of service, data processing agreements, or equivalent contractual commitments that we have accepted with them, and only as necessary to provide the service.
| Provider | What they do | Where |
| ------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| Microsoft Azure | Database hosting (PostgreSQL) | United States |
| Vercel | Frontend hosting | United States + global edge |
| Hetzner | API endpoints and bots | United States (Hetzner is a German company, but we use their US-based data centers) |
| Cloudflare | DNS, reverse proxy, security, DDoS protection, privacy-focused analytics | Global edge |
| Google Firebase | Authentication | United States |
| Google (SSO) | Optional Google sign-in | United States |
| Linden Lab (Second Life) | Inworld integration | United States |
This list may change over time as our infrastructure evolves. We'll update this policy to reflect material changes and, where required, notify registered users.
We may also disclose information if legally required (court order, subpoena, etc.) or if it's necessary to protect the safety of our users or the public.
If Allomancy is ever acquired or merged, your data may transfer to the new owner; we'd notify you first.
## 7. Where Your Data Is Stored and International Transfers [#7-where-your-data-is-stored-and-international-transfers]
Although Allomancy is a Brazilian company, **none of our servers or databases are located in Brazil**. All infrastructure is hosted in the United States, with Cloudflare also operating a global edge network for routing and security.
This means that when you use Allomancy (regardless of where you are located), your data is transferred to and stored in:
* **United States**: Firebase, Azure, Vercel, Google, Linden Lab, and Hetzner (we use Hetzner's US-based data centers, even though Hetzner itself is a German company)
* **Cloudflare's global edge**: for routing and security
Where required by law, we rely on appropriate safeguards for international transfers, such as Standard Contractual Clauses (SCCs), Data Processing Agreements, adequacy decisions, or other mechanisms permitted under the GDPR and the LGPD (Articles 33–36). You can request more details about the safeguards applicable to a specific provider by emailing [archis@allomancy.com](mailto:archis@allomancy.com).
## 8. How Long We Keep Data [#8-how-long-we-keep-data]
Our current retention periods are:
| Data | How long we keep it |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Account data (email, settings) | While your account is active; deleted within 30 days after closure |
| Store configuration | While your account is active; deleted within 30 days after closure |
| Transaction records | Indefinitely while the merchant's account is active; deleted within 90 days after the merchant closes their account, or sooner if the merchant requests deletion |
| API keys (hashed) | Until you revoke them |
| Audit logs | 24 months, including after your account closes |
| Account activity timestamps (e.g. last login) | Stored with your account; deleted within 30 days after closure |
| Support correspondence | Up to 36 months after the last message, unless needed longer for security, dispute resolution, abuse prevention, or legal claims |
| Technical/server logs | Usually retained for a short period for security, debugging, bot operations, and infrastructure operations. Some logs are controlled by our infrastructure providers; where we control them directly, we keep them only as long as needed for those purposes unless a longer period is required for security, abuse prevention, dispute resolution, or legal claims. |
| Backups | Up to 90 days on a rolling cycle |
| Cloudflare / Firebase telemetry | Controlled by those providers; see their policies |
**A note on audit logs.** We keep audit logs for 24 months even after you close your account. This is because they are necessary to investigate fraud, security incidents, and disputes, and to defend legal claims. Access is tightly restricted and we don't use them for anything else.
## 9. Security [#9-security]
We take reasonable, industry-standard steps to protect your data:
* TLS encryption for all connections
* Passwords never stored by us (handled by Firebase)
* API keys stored only as one-way hashes (we cannot recover the original key)
* DDoS protection and firewalling via Cloudflare
* Access controls, audit logging, and monitoring
* Production and non-production environments kept separate
No system is 100% secure. If a breach ever affects your data, we'll notify you and the appropriate authorities in line with GDPR (within 72 hours where required) and LGPD.
## 10. Your Rights [#10-your-rights]
You have strong rights over your personal data. These rights apply to all users; some are specific to where you live.
### Rights available to everyone [#rights-available-to-everyone]
* **Access**: ask what data we have about you
* **Correction**: fix anything inaccurate
* **Deletion**: ask us to delete your account and data. Some restrictions apply: for example, we may not be able to delete transaction records or audit log entries that merchants or Allomancy need to keep for legitimate purposes, such as completing transactions, handling disputes, preventing abuse, complying with legal obligations, or defending legal claims.
* **Portability**: get a copy of your data in a machine-readable format
* **Objection**: object to processing based on our legitimate interests
* **Withdraw consent**: where processing relies on consent
### If you are in the EU, UK, or EEA (GDPR) [#if-you-are-in-the-eu-uk-or-eea-gdpr]
You also have the right to **lodge a complaint with your national Data Protection Authority**. A list is available at [edpb.europa.eu](https://edpb.europa.eu/about-edpb/about-edpb/members_en).
### If you are in Brazil (LGPD) [#if-you-are-in-brazil-lgpd]
Under Article 18 of the LGPD, you also have the right to:
* Know which public and private entities we've shared your data with
* Know about the option to refuse consent and what happens if you do
* Complain to the **Autoridade Nacional de Proteção de Dados (ANPD)** at [gov.br/anpd](https://www.gov.br/anpd/)
### If you are in California [#if-you-are-in-california]
The California Consumer Privacy Act (CCPA/CPRA) may not formally apply to Allomancy based on our size and practices, but we voluntarily extend the following rights to California residents regardless:
* Know what personal information we collect and disclose
* Request deletion and correction
* Opt out of the sale or sharing of personal information for cross-context behavioral advertising (we don't sell personal information or share it for that purpose, so there is nothing to opt out of)
* Non-discrimination for exercising these rights
### How to exercise your rights [#how-to-exercise-your-rights]
Email **[archis@allomancy.com](mailto:archis@allomancy.com)**. We'll respond within the legal deadlines where a specific law applies, and within a reasonable time otherwise. As a general guide, this is usually 30 days under GDPR, 15 days under LGPD, and 45 days under CCPA/CPRA where it applies. We may need to verify your identity first. If we can't fully honor a request (for example, because of audit log retention), we'll explain why.
## 11. Legal Basis for Processing (GDPR & LGPD) [#11-legal-basis-for-processing-gdpr--lgpd]
If you're in the EU, UK, or Brazil, here's why we're legally allowed to process your data:
* **To perform our contract with you**: providing the service you signed up for *(GDPR Art. 6(1)(b) / LGPD Art. 7, V)*
* **Legitimate interests**: keeping the service secure, preventing fraud, maintaining audit logs *(GDPR Art. 6(1)(f) / LGPD Art. 7, IX)*
* **Legal obligations**: complying with laws that apply to us *(GDPR Art. 6(1)(c) / LGPD Art. 7, II)*
* **Your consent**: for optional features where we specifically ask *(GDPR Art. 6(1)(a) / LGPD Art. 7, I)*
## 12. Age Restrictions [#12-age-restrictions]
Allomancy is not intended for people **under 16 years old**. We don't knowingly collect data from anyone under 16. If you think a child under 16 has signed up, email [archis@allomancy.com](mailto:archis@allomancy.com) and we'll delete the account.
Second Life itself has its own age restrictions (set by Linden Lab), which apply separately.
## 13. Third-Party Services [#13-third-party-services]
Allomancy connects with services operated by Linden Lab, Google, Microsoft, Vercel, Hetzner, and Cloudflare. Their privacy practices are governed by their own policies, not ours. We recommend reading them if you're curious.
## 14. Changes to This Policy [#14-changes-to-this-policy]
If we update this policy in a meaningful way, we'll update the date at the top and let you know by email or through the service before the changes take effect. For most changes, continuing to use Allomancy after that means you accept the updated version. If a change requires your consent under applicable law, we'll ask for your consent before the change applies to you.
## 15. Contact [#15-contact]
Questions, requests, or concerns? Contact our Data Protection Officer:
**Archis Conundrum**\
Encarregado pelo Tratamento de Dados Pessoais\
Email: [archis@allomancy.com](mailto:archis@allomancy.com)
**Allomancy Desenvolvimento de Software LTDA**\
CNPJ: 60.078.642/0001-35\
Brazil
You can also complain to a data protection authority:
* **Brazil:** ANPD at [gov.br/anpd](https://www.gov.br/anpd/)
* **EU/EEA:** Your national data protection authority at [edpb.europa.eu](https://edpb.europa.eu/about-edpb/about-edpb/members_en)
* **UK:** ICO at [ico.org.uk](https://ico.org.uk/)
* **California:** CPPA at [cppa.ca.gov](https://cppa.ca.gov/)
# Terms of Service (/docs/terms-of-service)
**Effective Date:** April 25, 2026\
**Last Updated:** April 25, 2026
These are the Terms of Service for Allomancy. We've tried to keep them short, honest, and readable. If anything is unclear, email us at **[archis@allomancy.com](mailto:archis@allomancy.com)** and we'll explain.
These Terms are a binding agreement between you and Allomancy. Please read them before using the service.
## 1. Who We Are [#1-who-we-are]
**Allomancy Desenvolvimento de Software LTDA**\
CNPJ: 60.078.642/0001-35\
Headquartered in São Paulo, SP, Brazil.
Allomancy is a vending and management platform for residents of the virtual world Second Life. We help merchants run stores, track sales, manage products, process vendor transactions in Linden Dollars, host automated agents (bots), and access our third-party API.
> **About the name.** "Allomancy" is just our brand name. It has no connection to, affiliation with, or endorsement from any book, author, or media franchise. We are also not affiliated with, endorsed by, or sponsored by Linden Research, Inc. ("Linden Lab"), the operator of Second Life.
**Contact:** [archis@allomancy.com](mailto:archis@allomancy.com)
## 2. Some Terms Used in This Agreement [#2-some-terms-used-in-this-agreement]
To keep things clear, here are a few terms we use throughout these Terms:
* **Service**: the Allomancy platform, including the dashboard, inworld devices, scripts, automated agents (bots), API, and related utilities.
* **Merchant**: a Second Life resident who has registered an Allomancy account to run their store.
* **Buyer**: a Second Life resident who purchases from an Allomancy-powered vendor.
* **Manager**: a Second Life resident invited by a merchant to access the merchant's account with limited, role-based permissions through the dashboard.
* **Inworld device**: a scripted object placed inside Second Life by a merchant (such as a vendor or dispenser) that is part of the service.
* **Dashboard**: the Allomancy web application where merchants configure their stores and view their data.
* **L$**: Linden Dollars, the inworld currency of Second Life operated by Linden Lab.
## 3. Acceptance of These Terms [#3-acceptance-of-these-terms]
By creating an Allomancy account, accessing the dashboard, using any Allomancy-powered inworld device, or making any API call, you acknowledge that you have read these Terms, understood them, and agreed to be bound by them. You also agree to our [Privacy Policy](/docs/privacy-policy), which is part of this agreement.
If you do not agree, do not use the service.
## 4. Eligibility and Account Rules [#4-eligibility-and-account-rules]
### 4.1 Age requirement [#41-age-requirement]
You must be at least **16 years old** to use Allomancy. If you are under 16, you may not create an account or use the service.
### 4.2 Avatar binding [#42-avatar-binding]
Your Allomancy account is permanently tied to the Second Life avatar that registered it. If you switch to a new Second Life avatar (an alt or replacement account), you must register a new Allomancy account from scratch. We are not obligated to transfer accounts, subscription status, sales history, or any other data between Second Life avatars, even if you tell us both avatars belong to you.
Where supported by the dashboard, you may be able to export certain data (such as sales and customer CSVs) and import it into another account you own. Any migration of this kind is your responsibility and may not include all account data, including settings, active subscriptions, API keys, audit logs, or system history.
### 4.3 Multiple accounts [#43-multiple-accounts]
You may run multiple Allomancy accounts if each is tied to a different Second Life avatar. Each account is independent and is billed separately based on the inworld vendors associated with that specific avatar. Each account that opts in to a subscription must have its own active subscription.
### 4.4 Account access and managers [#44-account-access-and-managers]
You are responsible for keeping your dashboard credentials secure. Sharing your password or login credentials with anyone is strictly prohibited.
If you want others to help operate your store, you have two options:
* **Invite them as managers** through the dashboard, where you can grant role-based permissions to specific people.
* **Generate API keys** for technical integrations.
You remain fully responsible for any actions taken by managers you invite or by anyone using your API keys.
Allomancy is not liable for pricing changes, accidental zero-price sales, product changes, deleted product or sales data, store setting changes, profit-share configuration, customer-service actions, operational consequences, or other actions taken by managers you invite or by anyone using access you authorized, except to the extent caused by Allomancy's own security breach or willful misconduct. Be careful when granting permissions to managers, especially permissions that can affect product pricing, product data, sales records, profit shares, or other L$-related outcomes.
### 4.5 Account ownership [#45-account-ownership]
Allomancy accounts are tied to the Second Life avatar that registered them. Accounts may not be sold, gifted, assigned, or transferred to another person or another avatar. Where you export and import supported data into another account you own, you are transferring only the exported data, not ownership of the original Allomancy account.
## 5. The Service [#5-the-service]
### 5.1 What we provide [#51-what-we-provide]
Allomancy provides a platform that allows merchants to run vendors in Second Life, track sales, manage products, process vendor transactions in L$, host automated agents (bots) via our infrastructure, and access our third-party API for more complex integrations.
The specific features available to you depend on the tier of service you are on. Features and tier contents are described on the Allomancy website or dashboard and may evolve over time.
### 5.2 Tiers [#52-tiers]
Allomancy offers a non-subscribed tier and one or more paid subscription tiers:
* **Non-subscribed tier (free):** You can use the service at no upfront cost, but a 5% commission is automatically deducted from each completed sale through your Allomancy-powered vendors.
* **Subscription tiers:** Active subscriptions remove the 5% Allomancy transaction commission and add cumulative features. Each higher tier includes all features of the lower tiers.
The specific subscription prices, tier features, and commission rates are published on the Allomancy website or dashboard. Those are the controlling source for current pricing and tier contents.
> **Note:** Subscriptions remove the **Allomancy** commission only. Subscriptions do **not** remove fees charged by Linden Lab, Second Life Marketplace, payment processors, or any other third party.
## 6. Pricing, Payments, and Refunds [#6-pricing-payments-and-refunds]
### 6.1 Currency [#61-currency]
All payments to Allomancy are made strictly in Linden Dollars (L$). We do not accept, hold, or process credit cards, bank transfers, or any real-world currency.
### 6.2 How you pay [#62-how-you-pay]
* **Commissions** are automatically split and deducted from each L$ transaction at the moment the inworld sale occurs through your Allomancy-powered vendor.
* **Subscriptions** are paid as a flat L$ amount initiated from the dashboard. The payment is processed through any vendor you have rezzed inworld. If you do not have a rezzed vendor with debit permission granted, the subscription payment will not proceed.
### 6.3 Pricing changes [#63-pricing-changes]
We may change prices, tier contents, or commission rates at any time. For material changes affecting existing registered users, we will give at least **30 days' notice** before the change takes effect where practical. For active subscribers, new subscription pricing will apply at the next renewal. New customers see the new pricing immediately.
### 6.4 Refunds [#64-refunds]
**Subscriptions.** Subscription fees are non-refundable by default. We may, at our sole discretion, issue L$ refunds, account credits, or time extensions for accidental payments, double subscriptions, or technical billing errors. We do not provide pro-rata refunds for unused subscription time.
**Commissions.** Commissions are non-refundable. If you choose to refund a buyer for any reason, that is between you and the buyer, and it does not entitle you to a refund of the 5% commission Allomancy already collected on the original sale.
### 6.5 Failed renewal [#65-failed-renewal]
You are responsible for keeping at least one eligible Allomancy-powered vendor rezzed with valid debit permissions if you want your subscription to renew. If your subscription fails to renew (for example, because of insufficient L$ balance or because no rezzed vendor has debit permissions), your account automatically reverts to the non-subscribed tier. The 5% commission per sale resumes immediately. Your vendors continue to operate; there is no hard suspension.
### 6.6 No L$ balance held by Allomancy [#66-no-l-balance-held-by-allomancy]
Allomancy does not hold L$ balances, wallets, or escrow accounts on behalf of merchants. Commission splits happen instantaneously at the moment of sale. Subscriptions are paid up front and consumed over time. There are no L$ balances to refund or pay out at termination.
### 6.7 No bypass [#67-no-bypass]
Attempting to bypass, evade, reverse-engineer, or otherwise interfere with the automated commission collection (for example, by modifying scripts, using unauthorized devices, or using technical means to avoid the commission split) is a serious violation of these Terms. See Section 7.
## 7. Acceptable Use [#7-acceptable-use]
You agree not to use the service for any of the following:
1. **Violating Linden Lab's Terms of Service**, Community Standards, or any other Linden Lab policies.
2. **Selling content that infringes third-party intellectual property rights**, including but not limited to bootleg branded items, copyrighted media, or content ripped from other creators. You are solely responsible for the intellectual property rights of everything you sell through Allomancy.
3. **Fraud and financial misconduct**, including fake sales, money laundering, manipulating sales data, or using stolen L$.
4. **Illegal activity** under Brazilian law or any other applicable law.
5. **Reverse engineering or bypassing security**, including decompiling, attempting to extract, or attempting to circumvent security measures of Allomancy's scripts, inworld devices, bots, API, or dashboard.
6. **Bypassing the commission system**, as described in Section 6.7.
7. **Excessive or abusive load** on the API, bots, or infrastructure, including automated scraping, denial-of-service-like patterns, or rate-limit evasion.
8. **Reselling Allomancy's services** to third parties as if they were your own, including re-branding the dashboard or API.
9. **Sharing dashboard credentials** in violation of Section 4.4.
10. **Harassing, harming, or defrauding other Second Life residents**, including misusing buyer data for spam, harassment, or any non-business purpose.
11. **Impersonating Allomancy** or claiming endorsement or affiliation that does not exist.
12. **Causing harm to Allomancy's infrastructure**, including conduct that causes or risks causing our IP addresses, databases, servers, automated agents (bots), API endpoints, or integrations with Second Life to be rate-limited, blacklisted, blocked, or banned by Linden Lab or any other network provider.
13. **Brand and IP misuse.** "Allomancy" is just our brand name and has no connection to, affiliation with, or endorsement from any book, author, or media franchise. You may not use Allomancy's name, logos, or branding to imply such an affiliation, and you may not use the platform to sell content that infringes the intellectual property rights of any third party.
Violation of any of the above may result in suspension or termination of your account. For serious violations (fraud, illegal content, IP infringement, infrastructure harm, commission bypass, or violation of Linden Lab's Terms of Service), termination may be immediate and without notice. See Section 12.
## 8. Adult Content [#8-adult-content]
Adult and mature-rated content is permitted on Allomancy, provided that:
* You strictly comply with Linden Lab's Terms of Service, Community Standards, and Maturity Ratings.
* You do not advertise, display, or sell adult content from regions that are not rated for adult content.
* You comply with all applicable laws regarding the distribution of adult content.
If you violate Linden Lab's adult-content rules, you violate these Terms.
## 9. The Merchant-Buyer Relationship [#9-the-merchant-buyer-relationship]
Allomancy provides the platform that supports sales between merchants and buyers, including processing vendor transactions and the automatic commission split. Allomancy is not the seller of merchant products and is not a party to the transaction for the goods or content sold. You, the merchant, are solely responsible for:
* The products and content you sell
* Customer service, including refunds, replacements, redeliveries, and complaints
* Tax obligations arising from your sales
* Compliance with consumer protection laws that may apply to your relationship with your buyers
* Resolving any disputes with your buyers
Allomancy does not mediate disputes between merchants and buyers, does not process buyer-side refunds, and does not act as a guarantor of merchant performance.
For data protection purposes, the relationship between you, your buyers, and Allomancy with respect to buyer personal data is described in our [Privacy Policy](/docs/privacy-policy).
## 10. Intellectual Property [#10-intellectual-property]
### 10.1 Allomancy's intellectual property [#101-allomancys-intellectual-property]
Allomancy retains all right, title, and interest in and to the service, including the dashboard, inworld devices and scripts, automated agents (bots), API, documentation, branding, and any associated software or content. These Terms grant you a limited, non-exclusive, non-transferable, revocable license to use the service for your own legitimate business purposes during the term of your account. No other rights are granted, expressly or by implication.
### 10.2 Your intellectual property [#102-your-intellectual-property]
You retain ownership of your products and store content. You are generally responsible for the customer and sales records associated with your store, subject to applicable privacy and consumer protection laws and to Allomancy's limited platform-wide processing as described in our [Privacy Policy](/docs/privacy-policy). By using the service, you grant Allomancy a non-exclusive, royalty-free, worldwide license to host, store, transmit, display, and process your store data, product information, images, listings, transaction records, and related content, **solely as necessary to provide, secure, support, and improve the service**. This license terminates when your data is deleted in accordance with the retention schedule in our [Privacy Policy](/docs/privacy-policy).
### 10.3 Inworld scripted devices [#103-inworld-scripted-devices]
Allomancy inworld scripts and devices are licensed under Second Life's permission system, typically as **no-modify, no-transfer, copy**. Decompiling, reverse-engineering, attempting to extract, or attempting to modify protected scripts is prohibited.
If Allomancy provides 3D mesh objects (such as vendor shells designed for visual customization), those mesh objects are typically licensed as **copy and modify, no-transfer**, so you can adapt their appearance for your store. The scripts inside those meshes remain strictly no-modify.
Specific permissions may vary by item. The permissions shown in Second Life when you receive an item are the controlling source. After account termination, scripts, bots, API access, and service-dependent functionality may stop working, even if some objects remain in your Second Life inventory.
### 10.4 Feedback [#104-feedback]
If you provide feedback, suggestions, or ideas about the service, you grant Allomancy a perpetual, irrevocable, royalty-free license to use that feedback for any purpose, including incorporating it into the service. We are not obligated to compensate or credit you for feedback.
## 11. Service Availability and Modifications [#11-service-availability-and-modifications]
### 11.1 Best-effort availability [#111-best-effort-availability]
We aim for high availability and professional-grade uptime, but we do not provide a formal Service Level Agreement (SLA) and we do not guarantee uninterrupted service. The service is provided "as available," without service credits or downtime refunds.
### 11.2 Maintenance [#112-maintenance]
We may perform scheduled maintenance with reasonable notice when feasible, and we may perform emergency maintenance (such as security patches) without prior notice. We are not liable for service interruptions caused by maintenance.
### 11.3 Modifications and discontinuations [#113-modifications-and-discontinuations]
We may modify, update, or improve the service at any time, including changing features, performance characteristics, or technical implementation.
We may discontinue features or the service entirely. For material discontinuations affecting active subscribers, we will give at least **30 days' notice**. If we discontinue the service entirely, active subscriptions will stop renewing. Where practical, we will continue supporting active subscriptions until their existing expiration date or provide an appropriate credit, extension, refund, or other remedy at our discretion, subject to applicable law.
## 12. Termination [#12-termination]
### 12.1 Termination by you [#121-termination-by-you]
You may terminate your Allomancy account at any time by closing it through the dashboard or by emailing us at [archis@allomancy.com](mailto:archis@allomancy.com). There is no penalty for leaving. Active subscriptions stop renewing; the existing subscription period continues until expiration but is not refunded.
### 12.2 Termination by Allomancy for cause [#122-termination-by-allomancy-for-cause]
We may terminate or suspend your account in the following circumstances:
* **Serious violations** (fraud, illegal content, intellectual property infringement, commission bypass, infrastructure harm, violation of Linden Lab's Terms of Service, or other material violations of Section 7): termination may be **immediate and without notice**.
* **Less-severe violations** of these Terms: we will give you **at least 14 days' notice and an opportunity to cure** the violation before terminating.
### 12.3 No termination for inactivity [#123-no-termination-for-inactivity]
We do not automatically close accounts for inactivity. Inactive accounts remain open until you, the merchant, request closure or until termination occurs for cause.
### 12.4 Effect of termination [#124-effect-of-termination]
When your account is terminated for any reason:
* All Allomancy-powered inworld devices and bots associated with your account will cease functioning, because they will no longer be able to authenticate with our API.
* Your data will be deleted in accordance with the retention schedule in our [Privacy Policy](/docs/privacy-policy).
* Sections of these Terms that, by their nature, should survive termination (including intellectual property, indemnification, limitation of liability, and governing law) will continue to apply.
## 13. Linden Lab Dependency [#13-linden-lab-dependency]
Allomancy operates within the Second Life environment provided by Linden Research, Inc. ("Linden Lab"). **Allomancy is not affiliated with, endorsed by, or sponsored by Linden Lab.**
The service depends on Linden Lab's infrastructure, including but not limited to:
* The Second Life grid and region servers
* Linden Lab's economy and the Linden Dollar (L$) currency
* LSL (Linden Scripting Language) capabilities and APIs
* Marketplace and inventory systems
* Linden Lab's policies, terms, and pricing
Allomancy is **not responsible** for service interruptions, data loss, financial losses, or other harm caused by:
* Second Life grid outages, region downtime, or rolling restarts
* Changes to LSL, Linden Lab APIs, or grid behavior
* Suspension, ban, or deletion of merchant or buyer Second Life accounts by Linden Lab
* Changes to Linden Lab's policies, fees, or currency conversion rules
* Loss of inventory, L$, or inworld objects due to Linden Lab actions
You are responsible for complying with Linden Lab's Terms of Service, Community Standards, and other applicable Linden Lab policies. Violation of Linden Lab's policies is a violation of these Terms and may result in termination of your Allomancy account.
## 14. Warranties and Limitation of Liability [#14-warranties-and-limitation-of-liability]
### 14.1 Warranty disclaimer [#141-warranty-disclaimer]
To the maximum extent permitted by applicable law, the service is provided **"as is" and "as available"**, without warranties of any kind, express or implied, including warranties of merchantability, fitness for a particular purpose, non-infringement, or uninterrupted operation.
Where mandatory consumer protection laws (such as the Brazilian Consumer Defense Code, Lei nº 8.078/1990) apply, the warranties required by those laws cannot be waived and continue to apply.
### 14.2 Limitation of liability [#142-limitation-of-liability]
To the maximum extent permitted by applicable law, Allomancy's total liability to you, for any claim arising from or related to these Terms or the service, is limited to the total fees you paid to Allomancy in the **12 months** immediately preceding the event giving rise to the claim. For merchants on the non-subscribed tier who paid no fees in that period, liability is capped at the minimum amount permitted by applicable law.
In no event will Allomancy be liable for indirect, incidental, special, consequential, punitive, or exemplary damages, including loss of profits, loss of L$ revenue, loss of business opportunity, loss of data (beyond what is recoverable from backups), or loss of goodwill, even if Allomancy has been advised of the possibility of such damages.
These limitations apply to the maximum extent permitted by applicable law. Mandatory consumer protection rights (such as those under the Brazilian Consumer Defense Code) are not waived and apply where applicable.
### 14.3 No liability for Linden Lab and third parties [#143-no-liability-for-linden-lab-and-third-parties]
As described in Section 13, Allomancy has no liability for issues, downtime, or losses caused by Linden Lab, the Second Life grid, LSL changes, Linden Lab account actions, or any other third-party infrastructure on which the service depends.
### 14.4 Indemnification by merchants [#144-indemnification-by-merchants]
To the maximum extent permitted by applicable law, you agree to defend, indemnify, and hold harmless Allomancy and its officers, employees, and contractors from and against any claims, damages, losses, costs, or expenses (including reasonable legal fees) arising from:
* Your violation of these Terms or applicable law
* Your products or content sold through the service, including third-party intellectual property infringement claims
* Your violation of Linden Lab's Terms of Service or Community Standards
* Your tax obligations as a merchant
* Your use of buyer data outside of legitimate business purposes
* Any dispute between you and a buyer of your products
## 15. Legal Stuff [#15-legal-stuff]
### 15.1 Governing law [#151-governing-law]
These Terms are governed by the laws of the Federative Republic of Brazil, without regard to conflict-of-law principles.
### 15.2 Forum [#152-forum]
The courts of the city of São Paulo, state of São Paulo, Brazil have exclusive jurisdiction over any dispute arising from or related to these Terms or the service, except where mandatory consumer protection law (such as the Brazilian Consumer Defense Code or applicable EU consumer protection law) grants the consumer the right to file a claim in their own jurisdiction.
### 15.3 Good-faith resolution first [#153-good-faith-resolution-first]
Before filing a formal claim, you agree to first contact Allomancy at [archis@allomancy.com](mailto:archis@allomancy.com) to attempt to resolve the dispute in good faith. We will respond within 30 days. This requirement does not apply to claims for injunctive relief or where mandatory law allows immediate court action.
### 15.4 Notices [#154-notices]
We will send official notices to the email address on your Allomancy account. You are responsible for keeping that email address current. Notices to Allomancy must be sent to [archis@allomancy.com](mailto:archis@allomancy.com). Notices are deemed received on the date sent for email.
### 15.5 Assignment [#155-assignment]
We may assign these Terms (for example, in a merger, acquisition, or sale of assets) by giving you reasonable notice. You may not assign your rights or obligations under these Terms without our prior written consent.
### 15.6 Force majeure [#156-force-majeure]
Neither party will be liable for delays or failures caused by events beyond reasonable control, including natural disasters, war, terrorism, government action, internet or telecommunications outages, or third-party platform failures (such as Second Life or Linden Lab outages).
### 15.7 Independent contractors [#157-independent-contractors]
Nothing in these Terms creates a partnership, joint venture, employment, or agency relationship between you and Allomancy. Neither party may bind the other or make representations on the other's behalf.
### 15.8 Severability [#158-severability]
If any provision of these Terms is found unenforceable, the remaining provisions will continue in full force and effect.
### 15.9 No waiver [#159-no-waiver]
Our failure to enforce any provision of these Terms is not a waiver of our right to enforce it later.
### 15.10 Entire agreement [#1510-entire-agreement]
These Terms, together with the [Privacy Policy](/docs/privacy-policy), constitute the entire agreement between you and Allomancy regarding the service, superseding any prior agreements or understandings.
### 15.11 Language [#1511-language]
These Terms are written in English. The English version is the controlling version. If we make a translated version available in the future, it is provided for convenience only and the English version prevails in case of any conflict, except where applicable law requires otherwise.
## 16. Changes to These Terms [#16-changes-to-these-terms]
We may update these Terms from time to time. When we make material changes, we will update the "Last Updated" date at the top and notify affected registered users by email or through the service before the changes take effect. For most changes, continuing to use Allomancy after that means you accept the updated version. If a change requires your consent under applicable law, we will ask for your consent before the change applies to you.
## 17. Contact [#17-contact]
Questions, concerns, or notices regarding these Terms? Contact us:
**Allomancy Desenvolvimento de Software LTDA**\
CNPJ: 60.078.642/0001-35\
São Paulo, SP, Brazil\
Email: [archis@allomancy.com](mailto:archis@allomancy.com)
# Authentication (/docs/api/authentication)
Every request authenticates with a single API key sent in the `X-API-Key` header. The key identifies the store and carries the scopes that decide which endpoints it can reach.
## The X-API-Key header [#the-x-api-key-header]
Send the key as the sole value of the `X-API-Key` header on every request:
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/store \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/store",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch("https://integrations.allomancy.net/v1/store", {
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
});
console.log(await response.json());
```
```php
Send exactly one value. A request with no `X-API-Key` header, an empty value, or more than one value is rejected.
## Key format [#key-format]
A live key has three parts: a fixed prefix, a Base64Url payload, and a Base64Url signature, with the payload and signature joined by a dot.
```
allo_live_.
```
In the dashboard, a key is shown by its preview rather than its full value. The preview keeps the prefix and the last six characters:
```
allo_live_***Hk94w
```
The full key is shown only once, at the moment it is created. Allomancy stores only a hash of the key, so the full value can never be retrieved later. Treat the key like a password: anyone who holds it can act on the store within the key's scopes.
## The key lifecycle [#the-key-lifecycle]
The store owner manages keys in [Allomancy Stores](https://stores.allomancy.com) on the API Keys page.
* **Create.** Click Create API Key and give the key a name (3 to 128 characters, unique among the store's active keys), grant it one or more scopes, and optionally set a future expiry date. Leaving the expiry blank keeps the key active until it is revoked. The full key is displayed once on creation.
* **Edit.** Click on a key's row to change its name, scopes, or expiry date. A revoked key cannot be edited.
* **Revoke.** Click on a key's row to invalidate it immediately. Revocation cannot be undone, and any service using the key loses access at once. An optional reason can be recorded.
There is no separate rotate action in the dashboard. To rotate a key, create a new one and revoke the old one once the integration is using the replacement.
## Status codes [#status-codes]
Authentication and authorization surface through three status codes:
| Status | Meaning |
| ------ | ------------------------------------------------------------------------------------------------------- |
| `401` | The key is missing, malformed, unknown, expired, or revoked. |
| `403` | The key is valid, but it does not hold the scope the endpoint requires. See [Scopes](/docs/api/scopes). |
| `429` | The request was rate limited. Back off and retry later. |
## Rate limits [#rate-limits]
Two limits apply, both enforced server-side:
* **Authenticated requests are limited per key**, by default 60 requests per minute.
* **Failed authentication attempts are limited per client IP address**, by default 10 per minute. This throttles attempts to guess a key.
A `429` response means a limit was reached; back off and wait before retrying.
### Rate-limit headers [#rate-limit-headers]
Authenticated responses (both successful responses and a `429`) carry your current quota:
| Header | Meaning |
| --------------------- | -------------------------------------------------------------------------- |
| `RateLimit-Limit` | The request limit for the current window. |
| `RateLimit-Remaining` | How many requests remain in the current window. |
| `RateLimit-Policy` | The policy in the form `;w=`, for example `60;w=60`. |
An authenticated `429` additionally carries `Retry-After`, the number of seconds to wait before retrying. It is a conservative upper bound, the full window.
The unauthenticated `429` returned after too many failed key attempts carries only `Retry-After`. It does not include the `RateLimit-*` headers, because quota configuration is not disclosed to unauthenticated callers.
If the rate-limit backend is briefly unavailable, a request may be rejected with a `429` that carries `RateLimit-Policy` and `Retry-After` but omits the numeric `RateLimit-Limit` and `RateLimit-Remaining`. Treat missing quota numbers as unknown, not as unlimited.
# Categories (/docs/api/categories)
The categories endpoints manage a store's product categories and the products inside them. Field shapes are on the [Object reference](/docs/api/object-reference) page.
## List categories [#list-categories]
```
GET /v1/categories
```
Returns a cursor-paginated list of the store's categories. Scope: `categories:read`. Query parameters `limit` and `cursor` are optional; see [Pagination and errors](/docs/api/pagination-and-errors).
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/categories?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/categories",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/categories?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"data": [
{ "id": "b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b", "name": "Hats", "productCount": 12 }
],
"pagination": { "nextCursor": null, "previousCursor": null, "limit": 50 }
}
```
Each row is a [CategoryResponse](/docs/api/object-reference#categoryresponse). Status: `200`, or `400` for a bad pagination parameter.
## Get a category [#get-a-category]
```
GET /v1/categories/{id}
```
Returns one category by its UUID. Scope: `categories:read`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The response is a [CategoryResponse](/docs/api/object-reference#categoryresponse). Status: `200`, or `404` if no category matches.
## Create a category [#create-a-category]
```
POST /v1/categories
```
Creates a category. Scope: `categories:write`. The body is a [CreateCategoryRequest](/docs/api/object-reference#createcategoryrequest).
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/categories \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "name": "Hats" }'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/categories",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"name": "Hats"},
)
print(response.json())
```
```js
const response = await fetch("https://integrations.allomancy.net/v1/categories", {
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Hats" }),
});
console.log(await response.json());
```
```php
"Hats"]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
On success the response is `201 Created` with a `Location` header pointing at the new category, and the body is the created [CategoryResponse](/docs/api/object-reference#categoryresponse). Status: `201`, `400`, or `404`.
## Update a category [#update-a-category]
```
PUT /v1/categories/{id}
```
Renames a category. Scope: `categories:write`. The body is an [UpdateCategoryRequest](/docs/api/object-reference#updatecategoryrequest).
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b \
-X PUT \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "name": "Fancy hats" }'
```
```python
import requests
response = requests.put(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"name": "Fancy hats"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b",
{
method: "PUT",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Fancy hats" }),
},
);
console.log(await response.json());
```
```php
"Fancy hats"]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is the updated [CategoryResponse](/docs/api/object-reference#categoryresponse). Status: `200`, `400`, or `404`.
## Delete a category [#delete-a-category]
```
DELETE /v1/categories/{id}
```
Deletes a category. Scope: `categories:write`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b \
-X DELETE \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.delete(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.status_code)
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b",
{
method: "DELETE",
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(response.status);
```
```php
On success the response is `204 No Content`. Status: `204`, `400`, or `404`.
## Managing products in a category [#managing-products-in-a-category]
Two endpoints add or remove a single product. Both take no request body, both use `categories:write`, and both return `204 No Content`.
```
POST /v1/categories/{id}/products/{productId}
DELETE /v1/categories/{id}/products/{productId}
```
The `id` path parameter is the category's UUID and `productId` is the product's UUID.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37 \
-X POST \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.status_code)
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/categories/b4e1d7a2-3c8f-4d6e-9a1b-2c3d4e5f6a7b/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
{
method: "POST",
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(response.status);
```
```php
Send a `DELETE` to the same path to remove the product from the category. Status for both: `204`, `400`, or `404`.
## Related pages [#related-pages]
# Coupons (/docs/api/coupons)
The coupons endpoints read and manage a store's coupons, including the coupons a given avatar owns. A coupon's owner is the avatar who receives the coupon's commission (its `ownerProfit` share), which is distinct from the customer who applies the coupon to get the discount. Field shapes are on the [Object reference](/docs/api/object-reference) page.
## List an avatar's coupons [#list-an-avatars-coupons]
```
GET /v1/coupons/owner/{identifier}
```
Returns a cursor-paginated list of the coupons owned by one avatar, the avatar who receives each coupon's commission. Scope: `coupons:read`. The `{identifier}` is that avatar's Second Life username or UUID. Query parameters `limit` and `cursor` are optional; see [Pagination and errors](/docs/api/pagination-and-errors).
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/coupons/owner/resident.username?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/coupons/owner/resident.username",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/coupons/owner/resident.username?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
Each row is a [CouponResponse](/docs/api/object-reference#couponresponse). Status: `200`, `400`, or `404` if the avatar cannot be resolved.
## List all coupons [#list-all-coupons]
```
GET /v1/coupons
```
Returns a cursor-paginated list of every coupon in the store. Scope: `coupons:read`.
An optional `active` (boolean) query parameter narrows the list to active or inactive coupons. It applies to this endpoint only; [List an avatar's coupons](#list-an-avatars-coupons) does not take it.
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/coupons?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/coupons",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/coupons?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"data": [
{
"id": "e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
"code": "WELCOME10",
"active": true,
"bloggersSync": false,
"couponOwnerUuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"couponOwnerUsername": "resident.username",
"discount": 10,
"ownerProfit": 0,
"usesPerCustomer": 1,
"expiration": "2026-12-31T00:00:00Z"
}
],
"pagination": { "nextCursor": null, "previousCursor": null, "limit": 50 }
}
```
Each row is a [CouponResponse](/docs/api/object-reference#couponresponse). Status: `200`, or `400` for a bad pagination parameter.
## Get a coupon [#get-a-coupon]
```
GET /v1/coupons/{id}
```
Returns one coupon by its UUID. Scope: `coupons:read`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/coupons/e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/coupons/e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/coupons/e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The response is a [CouponResponse](/docs/api/object-reference#couponresponse). Status: `200`, or `404` if no coupon matches.
## Create a coupon [#create-a-coupon]
```
POST /v1/coupons
```
Creates a coupon. Scope: `coupons:write`. The body is a [CreateCouponRequest](/docs/api/object-reference#createcouponrequest), where `ownerIdentifier` is the owning avatar's Second Life username or UUID.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/coupons \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"code": "WELCOME10",
"discount": 10,
"ownerProfit": 0,
"ownerIdentifier": "resident.username",
"active": true,
"bloggersSync": false
}'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/coupons",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"code": "WELCOME10",
"discount": 10,
"ownerProfit": 0,
"ownerIdentifier": "resident.username",
"active": True,
"bloggersSync": False,
},
)
print(response.json())
```
```js
const response = await fetch("https://integrations.allomancy.net/v1/coupons", {
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "WELCOME10",
discount: 10,
ownerProfit: 0,
ownerIdentifier: "resident.username",
active: true,
bloggersSync: false,
}),
});
console.log(await response.json());
```
```php
"WELCOME10",
"discount" => 10,
"ownerProfit" => 0,
"ownerIdentifier" => "resident.username",
"active" => true,
"bloggersSync" => false,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
On success the response is `201 Created` with a `Location` header pointing at the new coupon, and the body is the created [CouponResponse](/docs/api/object-reference#couponresponse). Status: `201`, `400`, or `404`.
## Update a coupon [#update-a-coupon]
```
PUT /v1/coupons/{id}
```
Updates a coupon. Scope: `coupons:write`. The body is an [UpdateCouponRequest](/docs/api/object-reference#updatecouponrequest), and it replaces the coupon's fields: an omitted field is set to its default rather than left at its previous value. The coupon's owner is fixed at creation and cannot be changed here.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/coupons/e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b \
-X PUT \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"code": "WELCOME15",
"discount": 15,
"ownerProfit": 0,
"active": true,
"bloggersSync": false
}'
```
```python
import requests
response = requests.put(
"https://integrations.allomancy.net/v1/coupons/e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"code": "WELCOME15",
"discount": 15,
"ownerProfit": 0,
"active": True,
"bloggersSync": False,
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/coupons/e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
{
method: "PUT",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "WELCOME15",
discount: 15,
ownerProfit: 0,
active: true,
bloggersSync: false,
}),
},
);
console.log(await response.json());
```
```php
"WELCOME15",
"discount" => 15,
"ownerProfit" => 0,
"active" => true,
"bloggersSync" => false,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is the updated [CouponResponse](/docs/api/object-reference#couponresponse). Status: `200`, `400`, or `404`.
## Apply a coupon to a customer [#apply-a-coupon-to-a-customer]
```
POST /v1/customers/{identifier}/actions/apply-coupon
```
Pre-attaches a coupon to a customer so their next qualifying purchase honors it. Scope: `coupons:apply`. The `{identifier}` path parameter is the customer's Second Life username or avatar UUID. The body is an [ApplyCouponRequest](/docs/api/object-reference#applycouponrequest).
Identify the coupon with exactly one of `code`, `couponOwnerUuid`, or `couponId`. Set `scope` to `0` for a store-wide application or `1` for a single product; when `scope` is `1`, send the product's UUID in `productId`, and when `scope` is `0`, omit `productId`. The application lapses at `expiresAt`, which is the time of the request plus `expiresInHours` (optional, defaults to `24`, between `1` and `168`).
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/apply-coupon \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"code": "WELCOME10",
"scope": 1,
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"expiresInHours": 48
}'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/apply-coupon",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"code": "WELCOME10",
"scope": 1,
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"expiresInHours": 48,
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/apply-coupon",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "WELCOME10",
scope: 1,
productId: "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
expiresInHours: 48,
}),
},
);
console.log(await response.json());
```
```php
"WELCOME10",
"scope" => 1,
"productId" => "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"expiresInHours" => 48,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
```json
{
"couponId": "e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
"code": "WELCOME10",
"discount": 10,
"scope": "product",
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"customerUuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"customerUsername": "resident.username",
"expiresAt": "2026-06-17T14:00:00Z",
"replaced": false
}
```
The response is an [AppliedCouponResponse](/docs/api/object-reference#appliedcouponresponse). The `scope` field comes back as the string `"store"` or `"product"`, even though the request sends it as an integer. `replaced` is `true` when the application overwrote an earlier one for the same customer at the same scope. The coupon must be active and not expired. Status: `200`; `400` for an invalid body, a coupon-identifier or scope rule violation, an out-of-range `expiresInHours`, an unresolvable customer or owner, or a coupon that is inactive or expired; `404` if the store, coupon, or product is not found.
## Revoke a coupon from a customer [#revoke-a-coupon-from-a-customer]
```
POST /v1/customers/{identifier}/actions/revoke-coupon
```
Removes a coupon application from a customer. Scope: `coupons:apply`. The `{identifier}` path parameter is the customer's Second Life username or avatar UUID. The body is a [RevokeCouponRequest](/docs/api/object-reference#revokecouponrequest): the same coupon-identifier and `scope` rules as apply, without `expiresInHours`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/revoke-coupon \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"code": "WELCOME10",
"scope": 1,
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37"
}'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/revoke-coupon",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"code": "WELCOME10",
"scope": 1,
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/revoke-coupon",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "WELCOME10",
scope: 1,
productId: "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
}),
},
);
console.log(await response.json());
```
```php
"WELCOME10",
"scope" => 1,
"productId" => "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
```json
{
"revoked": true
}
```
The response is a [RevokedCouponResponse](/docs/api/object-reference#revokedcouponresponse). Revoke is idempotent: when no matching application exists, it still returns `200` with `revoked` set to `false`. Status: `200`; `400` for an invalid body or an unresolvable customer or owner; `404` if the store or coupon is not found.
## Related pages [#related-pages]
# Customer Tokens (/docs/api/customer-tokens)
The customer-tokens endpoint mints a short-lived, per-customer access token for the Allomancy customer page. Your key authenticates your store, you name a customer, and the response gives you a token plus a ready-formed link that opens the customer page as that resident. The minting store is always the one your API key authenticates, never a value in the request, and the customer is resolved from the identifier you send. Field shapes are on the [Object reference](/docs/api/object-reference) page.
## Mint a customer token [#mint-a-customer-token]
```
POST /v1/customer-tokens
```
Mints a customer access token for your store. Scope: `customertokens:mint`. The body is a [MintCustomerTokenRequest](/docs/api/object-reference#mintcustomertokenrequest).
Send the customer's Second Life username or avatar UUID in `customerIdentifier` (required); Allomancy resolves it to the resident server-side.
`collectiveId` is optional: set it to mint a token scoped to a collective instead of your store. Only the collective's owner store can mint this way, and only for a customer of your store. Omit it for a normal store-scoped token.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customer-tokens \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"customerIdentifier": "resident.username",
"collectiveId": null
}'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customer-tokens",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"customerIdentifier": "resident.username",
"collectiveId": None,
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customer-tokens",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
customerIdentifier: "resident.username",
collectiveId: null,
}),
},
);
console.log(await response.json());
```
```php
"resident.username",
"collectiveId" => null,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
```json
{
"token": "8Kd2zQ7Hs9...Qm4wZx",
"url": "https://customers.example.com/#ct=8Kd2zQ7Hs9...Qm4wZx",
"expiresAt": "2026-04-27T16:12:00Z"
}
```
The response is a [MintCustomerTokenResponse](/docs/api/object-reference#mintcustomertokenresponse). The `url` is ready to hand to the customer: it is your store's configured customer page origin with the token in the `#ct=` fragment. The customer page reads the token from that fragment and sends it as the `X-Customer-Token` header. The token is short-lived, so mint a fresh one when it expires.
Status: `200`. A `400` means the request body is missing, `customerIdentifier` is empty, or your store has no customer page origin configured (`Customer page origin is not configured.`). A `404` means the store or customer could not be resolved; when `collectiveId` is set, it also means your store is not the collective's owner, the collective does not exist, or the resident is not a customer of your store. A `429` (`The mint rate limit has been exceeded.`) means the per-key mint rate limit was hit; retry after a short wait.
## Related pages [#related-pages]
# Customers (/docs/api/customers)
The customers endpoints read a store's customers and act on them: update their details, change their store credit, grant a gift card, and ban or unban them. Field shapes are on the [Object reference](/docs/api/object-reference) page.
Throughout this page, `{identifier}` is a resident's Second Life username or avatar UUID. An identifier that cannot be resolved returns `400` (when it is malformed) or `404` (when no resident matches).
Watch the scopes: the credit endpoints use the `credits:*` scopes and the gift-card grant uses `giftcards:write`, even though they live under `/customers`. Each endpoint lists its own scope below.
## Reading customers [#reading-customers]
### List customers [#list-customers]
```
GET /v1/customers
```
Returns a cursor-paginated list of the store's customers. Scope: `customers:read`. Query parameters `limit` and `cursor` are optional; see [Pagination and errors](/docs/api/pagination-and-errors).
You can also narrow the list with optional filters:
* `newsSubscriber` (boolean): return only customers who are, or are not, subscribed to the store news kiosk.
* `spentMoreThan` (integer): return only customers whose lifetime spend (the same value the [Get a customer's spend](#get-a-customers-spend) endpoint returns as `sum`) is strictly greater than this amount in `L$`. A customer whose spend equals the value is excluded, and a customer with no recorded spend counts as 0, so any value of 0 or more excludes them.
Filters are optional and combine with AND.
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/customers?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/customers",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"data": [
{
"uuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"username": "resident.username",
"credit": 120,
"restrictedCredit": 0,
"discount": 5,
"newsSubscriber": true,
"isBanned": false
}
],
"pagination": { "nextCursor": null, "previousCursor": null, "limit": 50 }
}
```
Each row is a [CustomerResponse](/docs/api/object-reference#customerresponse). Status: `200`, or `400` for a bad pagination parameter.
### Get a customer [#get-a-customer]
```
GET /v1/customers/{identifier}
```
Returns one customer, including their ban state. Scope: `customers:read`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/customers/resident.username",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The response is a [CustomerResponse](/docs/api/object-reference#customerresponse). Status: `200`, `400`, or `404`.
### Get a customer's credit [#get-a-customers-credit]
```
GET /v1/customers/{identifier}/credit
```
Returns just the customer's credit balances. Scope: `credits:read`. These are the raw stored balances, so they can briefly read higher than the customer's spendable credit until an expiry sweep or the customer's next credit change reconciles any lots that have come due.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/credit \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/customers/resident.username/credit",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/credit",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"uuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"username": "resident.username",
"credit": 120,
"restrictedCredit": 0
}
```
The response is a [CustomerCreditResponse](/docs/api/object-reference#customercreditresponse). Status: `200`, `400`, or `404`.
### Get a customer's spend [#get-a-customers-spend]
```
GET /v1/customers/{identifier}/spend
```
Returns the customer's lifetime and last-year spend and how many products they own. Scope: `customers:read`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/spend \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/customers/resident.username/spend",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/spend",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"uuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"username": "resident.username",
"sum": 4820,
"lastYearSum": 1310,
"totalOwned": 17
}
```
The response is a [CustomerSpendResponse](/docs/api/object-reference#customerspendresponse). Status: `200`, `400`, or `404`.
## Updating a customer [#updating-a-customer]
```
PUT /v1/customers/{identifier}
```
Updates a customer's per-customer discount and news-kiosk subscription. Scope: `customers:write`. The body is an [UpdateCustomerRequest](/docs/api/object-reference#updatecustomerrequest).
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username \
-X PUT \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "discount": 10, "newsSubscriber": true }'
```
```python
import requests
response = requests.put(
"https://integrations.allomancy.net/v1/customers/resident.username",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"discount": 10, "newsSubscriber": True},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username",
{
method: "PUT",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ discount: 10, newsSubscriber: true }),
},
);
console.log(await response.json());
```
```php
10,
"newsSubscriber" => true,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is a [CustomerResponse](/docs/api/object-reference#customerresponse). Status: `200`, `400`, or `404`.
## Adjusting store credit [#adjusting-store-credit]
Two pairs of actions change a customer's credit: a **set** that writes an absolute balance, and an **increment** that applies a change. Each has a regular and a restricted variant. All four use the `credits:write` scope and return a [CustomerCreditResponse](/docs/api/object-reference#customercreditresponse). Store credit is tracked as expiring lots, each with an `expiresAt`, and that lot model is why set and increment behave differently.
Set is destructive. `set-credit` and `set-restricted-credit` write an absolute balance: in one transaction they close every open credit lot of that type (each marked superseded) and rewrite the balance. Pass `expiresAt` with a positive `credit` to create one covering lot for the whole new balance; with no `expiresAt`, the balance becomes permanent unlotted credit with no open lots. The flat response does not report that any lots were closed. To add expiring credit while preserving the customer's existing lots, use increment instead.
### Set credit [#set-credit]
```
POST /v1/customers/{identifier}/actions/set-credit
POST /v1/customers/{identifier}/actions/set-restricted-credit
```
Writes the balance to an absolute value. The body is a [SetCustomerCreditRequest](/docs/api/object-reference#setcustomercreditrequest), where `credit` is the new balance in `L$` and `expiresAt` is an optional ISO 8601 datetime. Supply `expiresAt` with a positive `credit` to place the whole new balance in one covering lot that expires then; omit it to leave the balance as permanent unlotted credit. An `expiresAt` already in the past is rejected with `400`, and the balance is left unchanged. Each variant is scoped to one credit type: setting regular credit does not touch restricted lots, and setting restricted credit does not touch regular lots.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/set-credit \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "credit": 200, "expiresAt": "2026-12-31T23:59:59Z" }'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/set-credit",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"credit": 200, "expiresAt": "2026-12-31T23:59:59Z"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/set-credit",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ credit: 200, expiresAt: "2026-12-31T23:59:59Z" }),
},
);
console.log(await response.json());
```
```php
200, "expiresAt" => "2026-12-31T23:59:59Z"]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
This example sets the balance to L$200 in one lot that expires on `2026-12-31T23:59:59Z`; omit `expiresAt` to leave the balance as permanent unlotted credit.
Status: `200`, `400`, or `404`.
### Increment credit [#increment-credit]
```
POST /v1/customers/{identifier}/actions/increment-credit
POST /v1/customers/{identifier}/actions/increment-restricted-credit
```
Applies a change to the current balance. The body is an [IncrementCustomerCreditRequest](/docs/api/object-reference#incrementcustomercreditrequest), where a positive `delta` adds credit and a negative `delta` removes it. Unlike set, increment never closes existing lots. A positive `delta` with an `expiresAt` adds one new lot for that amount; a positive `delta` with no `expiresAt` adds unlotted credit; a negative `delta` drains existing lots first-expiring-first. Use increment when you want to add expiring credit without disturbing the customer's current lots. An `expiresAt` cannot be sent with a negative `delta`, and doing so returns `400`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/increment-credit \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "delta": -50 }'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/increment-credit",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"delta": -50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/increment-credit",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ delta: -50 }),
},
);
console.log(await response.json());
```
```php
-50]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
To add expiring credit as a new lot, send a positive `delta` with an `expiresAt`:
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/increment-credit \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "delta": 100, "expiresAt": "2026-12-31T23:59:59Z" }'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/increment-credit",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"delta": 100, "expiresAt": "2026-12-31T23:59:59Z"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/increment-credit",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ delta: 100, expiresAt: "2026-12-31T23:59:59Z" }),
},
);
console.log(await response.json());
```
```php
100, "expiresAt" => "2026-12-31T23:59:59Z"]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
This adds a new lot of L$100 that expires on `2026-12-31T23:59:59Z`, leaving the customer's existing lots untouched.
Status: `200`, or `400` (for example when adding would exceed the credit limit, or when removing more than the customer holds), or `404`.
## Granting a gift card [#granting-a-gift-card]
```
POST /v1/customers/{identifier}/actions/grant-gift-card
```
Creates a gift card for the customer. Scope: `giftcards:write`. The body is a [GrantGiftCardRequest](/docs/api/object-reference#grantgiftcardrequest), where `credit` is the gift card balance in `L$`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/grant-gift-card \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "credit": 500, "texture": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b", "transfer": false }'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/grant-gift-card",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"credit": 500, "texture": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b", "transfer": False},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/grant-gift-card",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ credit: 500, texture: "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b", transfer: false }),
},
);
console.log(await response.json());
```
```php
500,
"texture" => "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
"transfer" => false,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is a [GiftCardResponse](/docs/api/object-reference#giftcardresponse). Status: `200`, `400`, or `404`. This grant is the only way to create a gift card through the API; see [Gift cards](/docs/api/gift-cards) for reading and updating one afterward.
## Banning and unbanning [#banning-and-unbanning]
```
POST /v1/customers/{identifier}/actions/ban
POST /v1/customers/{identifier}/actions/unban
```
Bans or unbans a customer. Scope: `customers:write`. Both return a [CustomerBanStateResponse](/docs/api/object-reference#customerbanstateresponse).
The ban action's body is an optional [BanCustomerRequest](/docs/api/object-reference#bancustomerrequest). Send a `reason` to record one, or omit the body to ban without a reason.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/ban \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "reason": "Chargeback fraud" }'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/ban",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"reason": "Chargeback fraud"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/ban",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({ reason: "Chargeback fraud" }),
},
);
console.log(await response.json());
```
```php
"Chargeback fraud"]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
```json
{
"uuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"username": "resident.username",
"isBanned": true
}
```
The unban action takes no body:
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/customers/resident.username/actions/unban \
-X POST \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/unban",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/actions/unban",
{
method: "POST",
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
Status for both: `200`, `400`, or `404`.
## Applying coupons [#applying-coupons]
Coupons can also be applied to or revoked from a customer through actions under `/v1/customers/{identifier}/actions/`. See [Coupons](/docs/api/coupons) for the apply-coupon and revoke-coupon endpoints.
## Related pages [#related-pages]
# Gift cards (/docs/api/gift-cards)
The gift cards endpoints read and manage individual gift cards. Field shapes are on the [Object reference](/docs/api/object-reference) page.
There is no store-wide gift-card list and no standalone create endpoint. A gift card is created by granting it to a customer with [`POST /v1/customers/{identifier}/actions/grant-gift-card`](/docs/api/customers#granting-a-gift-card); the endpoints here read and manage a gift card once it exists.
## List a customer's gift cards [#list-a-customers-gift-cards]
```
GET /v1/customers/{identifier}/giftcards
```
Returns a cursor-paginated list of one customer's gift cards. Scope: `giftcards:read`. The `{identifier}` is the customer's Second Life username or avatar UUID. Query parameters `limit` and `cursor` are optional; see [Pagination and errors](/docs/api/pagination-and-errors).
You can also narrow the list with optional filters:
* `active` (boolean): return only active, or only inactive, gift cards.
* `redeemed` (boolean): return only redeemed, or only unredeemed, gift cards.
Filters are optional and combine with AND.
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/customers/resident.username/giftcards?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/customers/resident.username/giftcards",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/customers/resident.username/giftcards?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"data": [
{
"id": "f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
"ownerUuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"ownerUsername": "resident.username",
"credit": 500,
"active": true,
"redeemed": false,
"transfer": false,
"texture": null,
"purchased": false
}
],
"pagination": { "nextCursor": null, "previousCursor": null, "limit": 50 }
}
```
Each row is a [GiftCardResponse](/docs/api/object-reference#giftcardresponse). Status: `200`, `400`, or `404` if the customer cannot be resolved.
## Get a gift card [#get-a-gift-card]
```
GET /v1/giftcards/{id}
```
Returns one gift card by its UUID. Scope: `giftcards:read`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The response is a [GiftCardResponse](/docs/api/object-reference#giftcardresponse). Status: `200`, or `404` if no gift card matches.
## Update a gift card [#update-a-gift-card]
```
PUT /v1/giftcards/{id}
```
Updates a gift card's balance, texture, transfer flag, and active state. Scope: `giftcards:write`. The body is an [UpdateGiftCardRequest](/docs/api/object-reference#updategiftcardrequest), and it replaces those fields: an omitted field is set to its default rather than left at its previous value.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b \
-X PUT \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{ "credit": 600, "texture": null, "transfer": true, "active": true }'
```
```python
import requests
response = requests.put(
"https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={"credit": 600, "texture": None, "transfer": True, "active": True},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
{
method: "PUT",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
credit: 600,
texture: null,
transfer: true,
active: true,
}),
},
);
console.log(await response.json());
```
```php
600,
"texture" => null,
"transfer" => true,
"active" => true,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is the updated [GiftCardResponse](/docs/api/object-reference#giftcardresponse). Status: `200`, `400`, or `404`.
## Disable a gift card [#disable-a-gift-card]
```
POST /v1/giftcards/{id}/actions/disable
```
Disables a gift card. Scope: `giftcards:write`. The action takes no request body.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b/actions/disable \
-X POST \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b/actions/disable",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/giftcards/f1a2b3c4-5d6e-4f70-8a9b-0c1d2e3f4a5b/actions/disable",
{
method: "POST",
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The response is the updated [GiftCardResponse](/docs/api/object-reference#giftcardresponse), now with `active` set to `false`. Status: `200`, `400`, or `404`.
## Related pages [#related-pages]
# Developer API (/docs/api)
The Allomancy Third-Party API lets an external service read and manage a single store programmatically: products, customers, categories, coupons, gift cards, store details, and loyalty tiers. It is a public REST API authenticated by a per-store API key.
## One key, one store [#one-key-one-store]
Every API key belongs to exactly one store and acts on that store's data. The store owner creates the key in [Allomancy Stores](https://stores.allomancy.com), grants it a set of scopes, and shares it with the integration. Send the key on every request in the `X-API-Key` header. See [Authentication](/docs/api/authentication) for the full key model.
## Base URL and versioning [#base-url-and-versioning]
All requests go to:
```
https://integrations.allomancy.net/v1
```
The API is HTTPS only, and every path sits under `/v1`. For example, store details live at `https://integrations.allomancy.net/v1/store`.
[//]: # "## Two surfaces"
[//]: #
[//]: # "The API has two halves:"
[//]: #
[//]: # "- **REST endpoints** (this reference): you call Allomancy to read and change store data."
[//]: # "- **Outbound webhooks**: Allomancy calls a URL you register when something happens in the store, such as a completed sale. Webhook documentation is published separately."
## Reading the responses [#reading-the-responses]
REST responses use JSON with camelCase field names. Enumerated fields serialize as integers, not strings: a discount type, for example, comes back as `0`, `1`, or `2` rather than a name. The Object reference page carries a value table for every such field.
## Where to go next [#where-to-go-next]
# Object reference (/docs/api/object-reference)
This page is the canonical schema dictionary for the API. Endpoint pages link here for the full shape of each object.
## Conventions [#conventions]
* **Field names are camelCase** on the wire. The tables below use that casing.
* **Enums serialize as integers**, not strings. A field typed as an enum comes back as a number; each enum's values are listed under [Enums](#enums). This is the authoritative statement of that rule for the whole REST reference.
* **Money fields are Linden Dollar (`L$`) amounts** expressed as integers.
* **UUIDs are Second Life UUIDs**, sent as their string GUID form (for example `a1b2c3d4-e5f6-4789-9abc-def012345678`).
* **Dates** are ISO 8601 timestamps in UTC (for example `2026-04-27T15:42:00Z`).
## Enums [#enums]
### DiscountType [#discounttype]
The `type` field of a [Discount](#discount).
| Value | Name |
| ----- | -------- |
| `0` | Percent |
| `1` | Flat |
| `2` | Override |
### CouponApplyScope [#couponapplyscope]
The `scope` field of an [ApplyCouponRequest](#applycouponrequest) and a [RevokeCouponRequest](#revokecouponrequest). The matching [AppliedCouponResponse](#appliedcouponresponse) returns the scope as a string (`"store"` or `"product"`) rather than the integer.
| Value | Name |
| ----- | ---------- |
| `0` | store-wide |
| `1` | product |
### VendorType [#vendortype]
The `type` field of a [VendorResponse](#vendorresponse) and an [UpdateVendorRequest](#updatevendorrequest).
| Value | Name |
| ----- | --------------- |
| `0` | Default |
| `1` | MultiMenuVendor |
| `2` | Multivendor |
| `4` | GachaVendor |
### ClickAction [#clickaction]
The `clickAction` field of a [VendorResponse](#vendorresponse) and an [UpdateVendorRequest](#updatevendorrequest). It sets the object's click action inworld.
| Value | Name |
| ----- | --------- |
| `0` | Touch |
| `1` | Sit |
| `2` | Buy |
| `3` | Pay |
| `4` | Open |
| `5` | Play |
| `6` | OpenMedia |
| `7` | Zoom |
| `8` | Disabled |
| `9` | Ignore |
## Shared types [#shared-types]
### Permissions [#permissions]
The Second Life permission flags on a product.
| Field | Type | Notes |
| ---------- | ------- | -------------------- |
| `copy` | boolean | Copy permission. |
| `modify` | boolean | Modify permission. |
| `transfer` | boolean | Transfer permission. |
### Discount [#discount]
A discount attached to a product.
| Field | Type | Notes |
| ----------- | ------- | --------------------------------------------------------- |
| `value` | integer | The discount amount. Its meaning depends on `type`. |
| `type` | integer | A [DiscountType](#discounttype) value (`0`, `1`, or `2`). |
| `startDate` | string | When the discount starts (UTC). |
| `endDate` | string | When the discount ends (UTC). |
```json
{
"value": 10,
"type": 0,
"startDate": "2026-04-01T00:00:00Z",
"endDate": "2026-05-01T00:00:00Z"
}
```
### PurchaseBonus [#purchasebonus]
The store's purchase-bonus configuration, returned on [StoreResponse](#storeresponse).
| Field | Type | Notes |
| ------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `baseBonus` | integer | Base bonus percentage (0 to 100). |
| `groupBonus` | integer | Bonus percentage for residents in the store group (0 to 100). |
| `creditExpiryDays` | integer or null | How long bonus credit lasts after a customer earns it, in days (1 to 3650). One expiry covers both bonus percentages; `null` when bonus credit never expires. |
### Profit share (request) [#profit-share-request]
The shape of each entry in a [ProductUpsertRequest](#productupsertrequest)'s `profitShares` array.
| Field | Type | Notes |
| ---------- | ------------- | ---------------------------------------------------------------------------------------------------- |
| `username` | string | The recipient's Second Life username. Accepted but not required; the share is matched on `userUuid`. |
| `userUuid` | string (UUID) | The recipient's Second Life UUID. This is the field the share is applied by. |
| `percent` | integer | The recipient's share percentage. |
### Profit share (response) [#profit-share-response]
The shape of each entry in a [ProductResponse](#productresponse)'s `profitShares` array.
| Field | Type | Notes |
| ---------- | ------------- | --------------------------------- |
| `userUuid` | string (UUID) | The recipient's Second Life UUID. |
| `percent` | integer | The recipient's share percentage. |
## Response bodies [#response-bodies]
### ProductResponse [#productresponse]
| Field | Type | Notes |
| ----------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `id` | string (UUID) | The product's UUID. |
| `name` | string | The product name. |
| `price` | integer or null | The listed price in `L$`. |
| `activePrice` | integer or null | The price after any active discount, in `L$`. |
| `image` | string (UUID) or null | The product image texture UUID. |
| `bloggable` | boolean | Whether bloggers can retrieve the product. |
| `active` | boolean | Whether the product is active. |
| `blockCredits` | boolean | Whether store credit is blocked for the product. |
| `item` | string or null | The inworld item name. |
| `limited` | boolean | Whether the product has a limited stock count. |
| `availableAmount` | integer | The remaining stock when `limited` is true. |
| `permissions` | [Permissions](#permissions) | The Second Life permission flags. |
| `discount` | [Discount](#discount) or null | The product's discount, if any. |
| `description` | string or null | The product description. |
| `demoId` | string (UUID) or null | The linked demo product's UUID. |
| `mpListing` | integer (int64) or null | The id of the associated Second Life Marketplace listing, or `null` if the product has no linked listing. |
| `profitShares` | [Profit share (response)](#profit-share-response)\[] | The product's profit-share split. |
### ProductDeliveryResponse [#productdeliveryresponse]
| Field | Type | Notes |
| ------------------- | ------------- | ----------------------------------------------------------------- |
| `productId` | string (UUID) | The delivered product's UUID. |
| `recipientUuid` | string (UUID) | The recipient's Second Life UUID. |
| `recipientUsername` | string | The recipient's Second Life username. |
| `queued` | boolean | Whether the delivery was queued. Delivery happens asynchronously. |
| `message` | string | The message sent with the delivery. |
### VendorResponse [#vendorresponse]
| Field | Type | Notes |
| ---------------- | --------------------- | --------------------------------------------------------------------------------------------- |
| `id` | string (UUID) | The vendor's UUID. |
| `name` | string | The vendor name. |
| `productId` | string (UUID) or null | The linked product's UUID, or null if none. |
| `productName` | string or null | The linked product's name, or null if none. |
| `syncName` | boolean | Whether the vendor name follows the linked product's name. |
| `active` | boolean | Whether the vendor is active. |
| `trackable` | boolean | Whether the vendor is findable with the unit locator HUD. |
| `price` | integer | The vendor price in `L$`. `-1` means the vendor inherits the linked product's price. |
| `imageFace` | integer or null | The object face the vendor image uses (0 to 7), or null. |
| `categoryId` | string (UUID) or null | The linked category's UUID for a multi vendor, or null. |
| `gachaId` | string (UUID) or null | The linked gacha's UUID for a gacha vendor, or null. |
| `groupOnly` | boolean | Whether the vendor sells only to residents in the store group. |
| `discount` | integer | The vendor discount percentage (0 to 100). |
| `groupDiscount` | integer | The discount percentage for residents in the store group (0 to 100). |
| `blockCredits` | boolean | Whether store credit is blocked at this vendor. |
| `clickAction` | integer or null | A [ClickAction](#clickaction) value, or null to keep the object's current click action. |
| `touchToAcquire` | boolean | For free products, whether touching the vendor delivers the item instead of opening the menu. |
| `type` | integer | A [VendorType](#vendortype) value. |
| `region` | string | The Second Life region the vendor last reported, or an empty string if unknown. |
| `position` | string | The vendor's last reported position, or an empty string if unknown. |
### CustomerResponse [#customerresponse]
| Field | Type | Notes |
| ------------------ | ------------- | ------------------------------------------------------------- |
| `uuid` | string (UUID) | The customer's Second Life UUID. |
| `username` | string | The customer's Second Life username. |
| `credit` | integer | Store credit balance in `L$`. |
| `restrictedCredit` | integer | Restricted store credit in `L$` (cannot be used for gifting). |
| `discount` | integer | The per-customer discount percentage. |
| `newsSubscriber` | boolean | Whether the customer subscribes to the store news kiosk. |
| `isBanned` | boolean | Whether the customer is banned from the store. |
### CustomerCreditResponse [#customercreditresponse]
| Field | Type | Notes |
| ------------------ | ------------- | ------------------------------------ |
| `uuid` | string (UUID) | The customer's Second Life UUID. |
| `username` | string | The customer's Second Life username. |
| `credit` | integer | Store credit balance in `L$`. |
| `restrictedCredit` | integer | Restricted store credit in `L$`. |
### CustomerSpendResponse [#customerspendresponse]
| Field | Type | Notes |
| ------------- | ------------- | ----------------------------------------- |
| `uuid` | string (UUID) | The customer's Second Life UUID. |
| `username` | string | The customer's Second Life username. |
| `sum` | integer | Lifetime spend in `L$`. |
| `lastYearSum` | integer | Spend over the last year in `L$`. |
| `totalOwned` | integer | The number of products the customer owns. |
### CustomerBanStateResponse [#customerbanstateresponse]
| Field | Type | Notes |
| ---------- | ------------- | ------------------------------------ |
| `uuid` | string (UUID) | The customer's Second Life UUID. |
| `username` | string | The customer's Second Life username. |
| `isBanned` | boolean | The ban state after the action. |
### MintCustomerTokenResponse [#mintcustomertokenresponse]
| Field | Type | Notes |
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `token` | string | The opaque customer access token. The customer page reads it from the `#ct=` fragment and sends it as the `X-Customer-Token` header. |
| `url` | string | A ready-formed link to the customer page: your store's configured customer page origin with the token in the `#ct=` fragment. |
| `expiresAt` | string | When the token expires (UTC). The token is short-lived. |
### CategoryResponse [#categoryresponse]
| Field | Type | Notes |
| -------------- | ------------- | --------------------------------------- |
| `id` | string (UUID) | The category's UUID. |
| `name` | string | The category name. |
| `productCount` | integer | The number of products in the category. |
### CouponResponse [#couponresponse]
| Field | Type | Notes |
| --------------------- | ------------- | ------------------------------------------------------ |
| `id` | string (UUID) | The coupon's UUID. |
| `code` | string | The coupon code. |
| `active` | boolean | Whether the coupon is active. |
| `bloggersSync` | boolean | Whether the coupon is synced with the blogger program. |
| `couponOwnerUuid` | string (UUID) | The owner's Second Life UUID. |
| `couponOwnerUsername` | string | The owner's Second Life username. |
| `discount` | integer | The customer discount percentage. |
| `ownerProfit` | integer | The owner profit percentage. |
| `usesPerCustomer` | integer | How many times each customer may use the coupon. |
| `expiration` | string | When the coupon expires (UTC). |
### GiftCardResponse [#giftcardresponse]
| Field | Type | Notes |
| --------------- | --------------------- | ---------------------------------------------------------- |
| `id` | string (UUID) | The gift card's UUID. |
| `ownerUuid` | string (UUID) | The owner's Second Life UUID. |
| `ownerUsername` | string | The owner's Second Life username. |
| `credit` | integer | The gift card balance in `L$`. |
| `active` | boolean | Whether the gift card is active. |
| `redeemed` | boolean | Whether the gift card has been redeemed. |
| `transfer` | boolean | Whether the gift card is transferable. |
| `texture` | string (UUID) or null | The gift card texture UUID. |
| `purchased` | boolean | Whether the gift card was purchased (rather than granted). |
### StoreResponse [#storeresponse]
| Field | Type | Notes |
| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------- |
| `id` | string (UUID) | The store's UUID. |
| `name` | string | The store name. |
| `logo` | string (UUID) or null | The store logo texture UUID. |
| `blogListed` | boolean | Whether the store is listed for bloggers. |
| `applicationOpen` | boolean | Whether blogger applications are open. |
| `bonus` | [PurchaseBonus](#purchasebonus) | The store's purchase-bonus configuration. |
| `locationSlUrl` | string | A Second Life map URL for the store location, or an empty string when no location is set. |
| `ownerUuid` | string (UUID) | The store owner's Second Life UUID. |
| `ownerUsername` | string | The store owner's Second Life username. |
### LoyaltyTierResponse [#loyaltytierresponse]
| Field | Type | Notes |
| -------------------- | ------- | ------------------------------------------------------------------------------------------- |
| `spendingThreshold` | integer | The `L$` spend required to reach the tier. |
| `discountPercentage` | integer | The discount the tier grants. |
| `isGroupOnly` | boolean | Whether the tier applies only to residents in the store group. |
| `isLastYearOnly` | boolean | Whether the threshold is measured against the last year's spend rather than lifetime spend. |
### AppliedCouponResponse [#appliedcouponresponse]
| Field | Type | Notes |
| ------------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `couponId` | string (UUID) | The applied coupon's UUID. |
| `code` | string | The coupon code. |
| `discount` | integer | The coupon's customer discount percentage. |
| `scope` | string | The application scope, `store` or `product`. The request sends `scope` as an integer; the response returns it as this string. |
| `productId` | string (UUID) or null | The product for a product-scope application; `null` for a store-wide application. |
| `customerUuid` | string (UUID) | The customer the coupon was applied to. |
| `customerUsername` | string | The Second Life username of the customer the coupon was applied to. |
| `expiresAt` | string | When the application lapses (UTC). |
| `replaced` | boolean | Whether this overwrote an existing application for the same customer at the same scope. |
### RevokedCouponResponse [#revokedcouponresponse]
| Field | Type | Notes |
| --------- | ------- | ---------------------------------------------------------------------- |
| `revoked` | boolean | Whether a matching application was removed. `false` when none existed. |
## Request bodies [#request-bodies]
### ProductUpsertRequest [#productupsertrequest]
The body for creating and updating a product.
| Field | Type | Notes |
| ----------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Required. The product name. |
| `price` | integer | The listed price in `L$`. Optional; defaults to `0`. |
| `image` | string (UUID) or null | The product image texture UUID. |
| `listed` | boolean | Whether the product is listed. Optional; defaults to `false`. |
| `bloggable` | boolean | Whether bloggers can retrieve the product. Optional; defaults to `false`. |
| `permissions` | [Permissions](#permissions) or null | The Second Life permission flags. Optional; if omitted, defaults to `{ copy: true, modify: false, transfer: false }`. |
| `discount` | [Discount](#discount) or null | The product's discount. |
| `availableAmount` | integer | The stock count when `limited` is true. Optional; defaults to `0`. |
| `limited` | boolean | Whether the product has a limited stock count. Optional; defaults to `false`. |
| `item` | string or null | The inworld item name. |
| `blockCredits` | boolean | Whether store credit is blocked for the product. Optional; defaults to `false`. |
| `description` | string or null | The product description. |
| `profitShares` | [Profit share (request)](#profit-share-request)\[] or null | The product's profit-share split. |
| `demoId` | string (UUID) or null | The linked demo product's UUID. |
| `active` | boolean | Whether the product is active. Optional; defaults to `true`. |
### DeliverProductRequest [#deliverproductrequest]
| Field | Type | Notes |
| --------------------- | -------------- | ------------------------------------------------------- |
| `recipientIdentifier` | string | Required. The recipient's Second Life UUID or username. |
| `message` | string or null | An optional message, up to 250 characters. |
### UpdateVendorRequest [#updatevendorrequest]
The body for updating a vendor. It replaces the vendor's fields, so an omitted field is set to its default rather than left at its previous value.
| Field | Type | Notes |
| ---------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The vendor name, at most 63 characters. Applied unless `syncName` is true and `type` is Default or GachaVendor; for those two types the name follows the linked product or gacha and this value is ignored. Every other type uses the value you send. |
| `productId` | string (UUID) or null | The product to link, or null for none. Must be a product in the store when changed. |
| `syncName` | boolean | Whether the vendor name follows the linked product's name. Optional; defaults to `false`. Governs whether `name` is applied. |
| `active` | boolean | Whether the vendor is active. Optional; defaults to `false`. |
| `trackable` | boolean | Whether the vendor is findable with the unit locator HUD. Optional; defaults to `false`. |
| `price` | integer or null | The vendor price in `L$`. Optional; omit or send null to inherit the linked product's price (stored as `-1`). A sent value must be `-1` (inherit) or `0` or greater. |
| `imageFace` | integer or null | The object face for the vendor image. Optional; must be 0 to 7 when sent. |
| `categoryId` | string (UUID) or null | The category to link for a multi vendor. Optional in general, but required when `type` is `1` (MultiMenuVendor). Must be a category in the store. |
| `gachaId` | string (UUID) or null | The gacha to link for a gacha vendor. Optional in general, but required when `type` is `4` (GachaVendor). Must be a gacha in the store. |
| `groupOnly` | boolean | Whether the vendor sells only to residents in the store group. Optional; defaults to `false`. |
| `discount` | integer | The vendor discount percentage. Optional; defaults to `0`; must be 0 to 100. |
| `groupDiscount` | integer | The store-group discount percentage. Optional; defaults to `0`; must be 0 to 100 and at least `discount`. |
| `blockCredits` | boolean | Whether store credit is blocked at this vendor. Optional; defaults to `false`. |
| `clickAction` | integer or null | A [ClickAction](#clickaction) value, or null to keep the object's current click action. Optional. |
| `touchToAcquire` | boolean | For free products, whether touching the vendor delivers the item. Optional; defaults to `false`. |
| `type` | integer | A [VendorType](#vendortype) value. Optional; defaults to `0` (Default). Values `1` and `4` require `categoryId` and `gachaId` respectively. |
### UpdateCustomerRequest [#updatecustomerrequest]
| Field | Type | Notes |
| ---------------- | ------- | --------------------------------------------------------------------------------------- |
| `discount` | integer | The per-customer discount percentage (0 to 100). Optional; defaults to `0`. |
| `newsSubscriber` | boolean | Whether the customer subscribes to the store news kiosk. Optional; defaults to `false`. |
### SetCustomerCreditRequest [#setcustomercreditrequest]
| Field | Type | Notes |
| ----------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `credit` | integer | The new credit balance in `L$` (0 or greater). Optional; defaults to `0`. |
| `expiresAt` | string or null | Optional ISO 8601 datetime. When set with a positive `credit`, the whole new balance is placed in one lot that expires at this time. Must be in the future; a past value is rejected with `400`. Omit for a balance that does not expire. |
### IncrementCustomerCreditRequest [#incrementcustomercreditrequest]
| Field | Type | Notes |
| ----------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `delta` | integer | Required; must be non-zero. The change to apply, in `L$`. Positive adds credit, negative removes it. |
| `expiresAt` | string or null | Optional ISO 8601 datetime. When set with a positive `delta`, the added credit forms one lot that expires at this time. Cannot be combined with a negative `delta`, which returns `400`. Omit for credit that does not expire. |
### GrantGiftCardRequest [#grantgiftcardrequest]
| Field | Type | Notes |
| ---------- | --------------------- | --------------------------------------------------------------------- |
| `credit` | integer | Required; `1` or greater. The gift card balance in `L$`. |
| `texture` | string (UUID) or null | The gift card texture UUID. |
| `transfer` | boolean | Whether the gift card is transferable. Optional; defaults to `false`. |
### BanCustomerRequest [#bancustomerrequest]
The body is optional on the ban action.
| Field | Type | Notes |
| -------- | -------------- | --------------------------------------------- |
| `reason` | string or null | An optional ban reason, up to 500 characters. |
### MintCustomerTokenRequest [#mintcustomertokenrequest]
| Field | Type | Notes |
| -------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerIdentifier` | string | Required. The customer's Second Life username or avatar UUID, resolved server-side. |
| `collectiveId` | string (UUID) or null | Optional. When set, mints a token scoped to a collective instead of the store; only the collective's owner store can mint this way, and only for a customer of the authenticated store. Omit for a store-scoped token. |
### CreateCategoryRequest [#createcategoryrequest]
| Field | Type | Notes |
| ------ | ------ | ------------------------------------------------- |
| `name` | string | Required. The category name, 1 to 100 characters. |
### UpdateCategoryRequest [#updatecategoryrequest]
| Field | Type | Notes |
| ------ | ------ | ------------------------------------------------- |
| `name` | string | Required. The category name, 1 to 100 characters. |
### CreateCouponRequest [#createcouponrequest]
| Field | Type | Notes |
| ----------------- | ------- | ------------------------------------------------------------------------------------- |
| `code` | string | Required. The coupon code. |
| `discount` | integer | The customer discount percentage. Optional; defaults to `0`. |
| `ownerProfit` | integer | The owner profit percentage. Optional; defaults to `0`. |
| `ownerIdentifier` | string | Required. The owner's Second Life UUID or username; it must resolve to an avatar. |
| `active` | boolean | Whether the coupon is active. Optional; defaults to `true`. |
| `bloggersSync` | boolean | Whether the coupon is synced with the blogger program. Optional; defaults to `false`. |
### UpdateCouponRequest [#updatecouponrequest]
The owner cannot be changed on update; it stays as set at creation.
| Field | Type | Notes |
| -------------- | ------- | ------------------------------------------------------------------------------------- |
| `code` | string | Required. The coupon code. |
| `discount` | integer | The customer discount percentage. Optional; defaults to `0`. |
| `ownerProfit` | integer | The owner profit percentage. Optional; defaults to `0`. |
| `active` | boolean | Whether the coupon is active. Optional; defaults to `true`. |
| `bloggersSync` | boolean | Whether the coupon is synced with the blogger program. Optional; defaults to `false`. |
### UpdateGiftCardRequest [#updategiftcardrequest]
| Field | Type | Notes |
| ---------- | --------------------- | ------------------------------------------------------------------------ |
| `credit` | integer | The gift card balance in `L$` (0 or greater). Optional; defaults to `0`. |
| `texture` | string (UUID) or null | The gift card texture UUID. |
| `transfer` | boolean | Whether the gift card is transferable. Optional; defaults to `false`. |
| `active` | boolean | Whether the gift card is active. Optional; defaults to `false`. |
### ApplyCouponRequest [#applycouponrequest]
The body for [Apply a coupon to a customer](/docs/api/coupons#apply-a-coupon-to-a-customer). Provide exactly one of `code`, `couponOwnerUuid`, or `couponId` to identify the coupon.
| Field | Type | Notes |
| ----------------- | --------------------- | ----------------------------------------------------------------------------------------------------------- |
| `code` | string or null | The coupon code. One of the three coupon identifiers; provide exactly one. |
| `couponOwnerUuid` | string (UUID) or null | The coupon owner's Second Life UUID. One of the three coupon identifiers; provide exactly one. |
| `couponId` | string (UUID) or null | The coupon's UUID. One of the three coupon identifiers; provide exactly one. |
| `scope` | integer | Required. A [CouponApplyScope](#couponapplyscope) value: `0` store-wide, `1` product. |
| `productId` | string (UUID) or null | The product to scope to. Required when `scope` is `1`; must be omitted when `scope` is `0`. |
| `expiresInHours` | integer or null | How long the application lasts before it lapses. Optional; defaults to `24`; must be between `1` and `168`. |
### RevokeCouponRequest [#revokecouponrequest]
The body for [Revoke a coupon from a customer](/docs/api/coupons#revoke-a-coupon-from-a-customer). The same coupon-identifier and scope rules as [ApplyCouponRequest](#applycouponrequest), without `expiresInHours`.
| Field | Type | Notes |
| ----------------- | --------------------- | ---------------------------------------------------------------------------------------------- |
| `code` | string or null | The coupon code. One of the three coupon identifiers; provide exactly one. |
| `couponOwnerUuid` | string (UUID) or null | The coupon owner's Second Life UUID. One of the three coupon identifiers; provide exactly one. |
| `couponId` | string (UUID) or null | The coupon's UUID. One of the three coupon identifiers; provide exactly one. |
| `scope` | integer | Required. A [CouponApplyScope](#couponapplyscope) value: `0` store-wide, `1` product. |
| `productId` | string (UUID) or null | The product to scope to. Required when `scope` is `1`; must be omitted when `scope` is `0`. |
# Pagination and errors (/docs/api/pagination-and-errors)
List endpoints return a cursor-paginated envelope, and every error uses the same problem+json shape. This page covers both.
## Cursor pagination [#cursor-pagination]
A list response wraps the rows in a `data` array alongside a `pagination` object:
```json
{
"data": [],
"pagination": {
"nextCursor": "Tjo3ZDJlNGM4NC05YTZmLTFiNWU4ZDBjMmEzNw",
"previousCursor": null,
"limit": 10
}
}
```
### Parameters [#parameters]
Both parameters are optional query-string values:
* **`limit`**: how many rows to return per page. Defaults to `100`, and must be between `1` and `1000`.
* **`cursor`**: an opaque token marking a position in the list. Omit it to start from the first page.
### Paging through results [#paging-through-results]
Each response returns the cursors for the adjacent pages. To move forward, send the `nextCursor` value back as the `cursor` parameter:
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/products?limit=10&cursor=Tjo3ZDJlNGM4NC05YTZmLTFiNWU4ZDBjMmEzNw" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/products",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 10, "cursor": "Tjo3ZDJlNGM4NC05YTZmLTFiNWU4ZDBjMmEzNw"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/products?limit=10&cursor=Tjo3ZDJlNGM4NC05YTZmLTFiNWU4ZDBjMmEzNw",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
`previousCursor` pages backward the same way. When `nextCursor` is `null`, you have reached the last page. Cursors are opaque: pass them back exactly as received, and do not construct or decode them yourself.
### Filtering [#filtering]
Some list endpoints accept optional filter query parameters, documented on each endpoint's own page. The API filters the result set; it does not sort it.
Because the cursor encodes only a position, it does not carry your filters. Re-send the same filter parameters on every page request so each page is drawn from the same filtered set. Changing the filters starts a new result set, so begin again without a cursor.
### Pagination errors [#pagination-errors]
A malformed pagination request returns `400` with one of these messages:
| Condition | Message |
| ---------------------------- | ----------------------------------- |
| `limit` outside 1 to 1000 | `Limit must be between 1 and 1000.` |
| `cursor` longer than allowed | `Cursor is too long.` |
| `cursor` cannot be decoded | `Cursor is invalid.` |
## Error format [#error-format]
Errors use the RFC 9457 problem+json format, served with the `application/problem+json` content type:
```json
{
"status": 400,
"title": "Bad Request",
"detail": "Limit must be between 1 and 1000.",
"instance": "/v1/products",
"errors": ["Limit must be between 1 and 1000."]
}
```
The fields are:
* **`status`**: the HTTP status code.
* **`title`**: a short label for the status, such as `Bad Request` or `Not Found`.
* **`detail`**: a human-readable description. When several validation messages apply, they are joined into one string.
* **`instance`**: the path of the request that produced the error.
* **`errors`**: an array of the individual validation messages. This field is present only when there are validation messages; a `404` for a missing resource, for example, omits it.
A `404` uses the same shape with the title `Not Found`:
```json
{
"status": 404,
"title": "Not Found",
"detail": "The requested resource was not found.",
"instance": "/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37"
}
```
### Where errors come from [#where-errors-come-from]
* **`400` and `404`** come from the request itself: a `400` for invalid input (a bad pagination parameter, a request body that fails validation) and a `404` for a resource or resident that cannot be found. Both carry the problem+json body above.
* **`401`, `403`, and `429`** come from the authentication, authorization, and rate-limiting layers before the endpoint runs. For these, rely on the status code and its meaning (see [Authentication](/docs/api/authentication)) rather than a response body.
# Products (/docs/api/products)
The products endpoints read and manage a store's catalog and deliver a product to a resident inworld. Every product field is described on the [Object reference](/docs/api/object-reference) page.
## List products [#list-products]
```
GET /v1/products
```
Returns a cursor-paginated list of the store's active products. Scope: `products:read`.
Query parameters: `limit` and `cursor`, both optional. See [Pagination and errors](/docs/api/pagination-and-errors) for the envelope and cursor rules.
You can also narrow the list with optional filters:
* `listed` (boolean): return only listed, or only unlisted, products.
* `bloggable` (boolean): return only bloggable, or only non-bloggable, products.
* `category` (UUID): return only products in a category. Pass a category's UUID from [List categories](/docs/api/categories), not its name.
* `search` (string): return only products whose name or description contains the term. The match is case-insensitive and partial, and a product matches if the term appears in either field, so a product with no description is still found by its name. A `%` or `_` in the term matches that character literally rather than as a wildcard, so searching `50%` finds products whose text contains `50%`, not `500`. An omitted, empty, or whitespace-only term applies no search filter.
Filters are optional and combine with AND, so sending several narrows the list to products matching all of them. For example, `GET /v1/products?search=crimson&listed=true&limit=50` returns up to 50 listed products whose name or description contains `crimson`.
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/products?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/products",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/products?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"data": [
{
"id": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"name": "Fancy hat",
"price": 250,
"activePrice": 225,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"bloggable": true,
"active": true,
"blockCredits": false,
"item": "Fancy hat",
"limited": false,
"availableAmount": 0,
"permissions": { "copy": true, "modify": false, "transfer": true },
"discount": { "value": 10, "type": 0, "startDate": "2026-04-01T00:00:00Z", "endDate": "2026-05-01T00:00:00Z" },
"description": "A very fancy hat.",
"demoId": null,
"mpListing": 4242,
"profitShares": [
{ "userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e", "percent": 10 }
]
}
],
"pagination": {
"nextCursor": "Tjo3ZDJlNGM4NC05YTZmLTFiNWU4ZDBjMmEzNw",
"previousCursor": null,
"limit": 50
}
}
```
Each row is a [ProductResponse](/docs/api/object-reference#productresponse). Status: `200`. A bad pagination parameter returns `400`.
## Get a product [#get-a-product]
```
GET /v1/products/{id}
```
Returns one product by its UUID. Scope: `products:read`.
Path parameter: `id`, the product's UUID.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37 \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"id": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"name": "Fancy hat",
"price": 250,
"activePrice": 225,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"bloggable": true,
"active": true,
"blockCredits": false,
"item": "Fancy hat",
"limited": false,
"availableAmount": 0,
"permissions": { "copy": true, "modify": false, "transfer": true },
"discount": { "value": 10, "type": 0, "startDate": "2026-04-01T00:00:00Z", "endDate": "2026-05-01T00:00:00Z" },
"description": "A very fancy hat.",
"demoId": null,
"mpListing": 4242,
"profitShares": [
{ "userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e", "percent": 10 }
]
}
```
The response is a [ProductResponse](/docs/api/object-reference#productresponse). The `discount.type` field is an integer: see [DiscountType](/docs/api/object-reference#discounttype). Status: `200`, or `404` if no product matches.
## Create a product [#create-a-product]
```
POST /v1/products
```
Creates a product. Scope: `products:write`. The body is a [ProductUpsertRequest](/docs/api/object-reference#productupsertrequest).
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/products \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"name": "Fancy hat",
"price": 250,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"listed": true,
"bloggable": true,
"permissions": { "copy": true, "modify": false, "transfer": true },
"discount": { "value": 10, "type": 0, "startDate": "2026-04-01T00:00:00Z", "endDate": "2026-05-01T00:00:00Z" },
"availableAmount": 0,
"limited": false,
"item": "Fancy hat",
"blockCredits": false,
"description": "A very fancy hat.",
"profitShares": [
{ "username": "partner.resident", "userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e", "percent": 10 }
],
"demoId": null,
"active": true
}'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/products",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"name": "Fancy hat",
"price": 250,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"listed": True,
"bloggable": True,
"permissions": {"copy": True, "modify": False, "transfer": True},
"discount": {
"value": 10,
"type": 0,
"startDate": "2026-04-01T00:00:00Z",
"endDate": "2026-05-01T00:00:00Z",
},
"availableAmount": 0,
"limited": False,
"item": "Fancy hat",
"blockCredits": False,
"description": "A very fancy hat.",
"profitShares": [
{
"username": "partner.resident",
"userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"percent": 10,
}
],
"demoId": None,
"active": True,
},
)
print(response.json())
```
```js
const response = await fetch("https://integrations.allomancy.net/v1/products", {
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Fancy hat",
price: 250,
image: "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
listed: true,
bloggable: true,
permissions: { copy: true, modify: false, transfer: true },
discount: {
value: 10,
type: 0,
startDate: "2026-04-01T00:00:00Z",
endDate: "2026-05-01T00:00:00Z",
},
availableAmount: 0,
limited: false,
item: "Fancy hat",
blockCredits: false,
description: "A very fancy hat.",
profitShares: [
{
username: "partner.resident",
userUuid: "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
percent: 10,
},
],
demoId: null,
active: true,
}),
});
console.log(await response.json());
```
```php
"Fancy hat",
"price" => 250,
"image" => "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"listed" => true,
"bloggable" => true,
"permissions" => ["copy" => true, "modify" => false, "transfer" => true],
"discount" => [
"value" => 10,
"type" => 0,
"startDate" => "2026-04-01T00:00:00Z",
"endDate" => "2026-05-01T00:00:00Z",
],
"availableAmount" => 0,
"limited" => false,
"item" => "Fancy hat",
"blockCredits" => false,
"description" => "A very fancy hat.",
"profitShares" => [
[
"username" => "partner.resident",
"userUuid" => "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"percent" => 10,
],
],
"demoId" => null,
"active" => true,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
On success the response is `201 Created` with a `Location` header pointing at the new product, and the body is the created [ProductResponse](/docs/api/object-reference#productresponse). A missing or invalid body returns `400`; a missing store returns `404`.
## Update a product [#update-a-product]
```
PUT /v1/products/{id}
```
Replaces a product's fields. Scope: `products:write`. The body is a [ProductUpsertRequest](/docs/api/object-reference#productupsertrequest).
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37 \
-X PUT \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"name": "Fancy hat",
"price": 300,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"listed": true,
"bloggable": true,
"permissions": { "copy": true, "modify": false, "transfer": true },
"discount": { "value": 10, "type": 0, "startDate": "2026-04-01T00:00:00Z", "endDate": "2026-05-01T00:00:00Z" },
"availableAmount": 0,
"limited": false,
"item": "Fancy hat",
"blockCredits": false,
"description": "A very fancy hat.",
"profitShares": [
{ "username": "partner.resident", "userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e", "percent": 10 }
],
"demoId": null,
"active": true
}'
```
```python
import requests
response = requests.put(
"https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"name": "Fancy hat",
"price": 300,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"listed": True,
"bloggable": True,
"permissions": {"copy": True, "modify": False, "transfer": True},
"discount": {
"value": 10,
"type": 0,
"startDate": "2026-04-01T00:00:00Z",
"endDate": "2026-05-01T00:00:00Z",
},
"availableAmount": 0,
"limited": False,
"item": "Fancy hat",
"blockCredits": False,
"description": "A very fancy hat.",
"profitShares": [
{
"username": "partner.resident",
"userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"percent": 10,
}
],
"demoId": None,
"active": True,
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
{
method: "PUT",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Fancy hat",
price: 300,
image: "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
listed: true,
bloggable: true,
permissions: { copy: true, modify: false, transfer: true },
discount: {
value: 10,
type: 0,
startDate: "2026-04-01T00:00:00Z",
endDate: "2026-05-01T00:00:00Z",
},
availableAmount: 0,
limited: false,
item: "Fancy hat",
blockCredits: false,
description: "A very fancy hat.",
profitShares: [
{
username: "partner.resident",
userUuid: "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
percent: 10,
},
],
demoId: null,
active: true,
}),
},
);
console.log(await response.json());
```
```php
"Fancy hat",
"price" => 300,
"image" => "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"listed" => true,
"bloggable" => true,
"permissions" => ["copy" => true, "modify" => false, "transfer" => true],
"discount" => [
"value" => 10,
"type" => 0,
"startDate" => "2026-04-01T00:00:00Z",
"endDate" => "2026-05-01T00:00:00Z",
],
"availableAmount" => 0,
"limited" => false,
"item" => "Fancy hat",
"blockCredits" => false,
"description" => "A very fancy hat.",
"profitShares" => [
[
"username" => "partner.resident",
"userUuid" => "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"percent" => 10,
],
],
"demoId" => null,
"active" => true,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is the updated [ProductResponse](/docs/api/object-reference#productresponse). Status: `200`, `400` for an invalid body, or `404` if no product matches.
## Deliver a product [#deliver-a-product]
```
POST /v1/products/{id}/actions/deliver
```
Delivers a product to a resident inworld. Scope: `products:deliver`. The body is a [DeliverProductRequest](/docs/api/object-reference#deliverproductrequest), where `recipientIdentifier` is the resident's Second Life username or UUID, and `message` is an optional note delivered with the product.
The product must exist in the store and be active.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37/actions/deliver \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"recipientIdentifier": "resident.username",
"message": "Thanks for shopping with us."
}'
```
```python
import requests
response = requests.post(
"https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37/actions/deliver",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"recipientIdentifier": "resident.username",
"message": "Thanks for shopping with us.",
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/products/3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37/actions/deliver",
{
method: "POST",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
recipientIdentifier: "resident.username",
message: "Thanks for shopping with us.",
}),
},
);
console.log(await response.json());
```
```php
"resident.username",
"message" => "Thanks for shopping with us.",
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
```json
{
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"recipientUuid": "c7d8e9f0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"recipientUsername": "resident.username",
"queued": true,
"message": "Thanks for shopping with us."
}
```
Delivery is queued and happens asynchronously, so the response returns `queued: true` rather than waiting for the resident to receive the item. The response is a [ProductDeliveryResponse](/docs/api/object-reference#productdeliveryresponse). Status: `200`, `400` for an invalid body or unresolvable recipient, or `404` if the product does not exist or is not active.
## Related pages [#related-pages]
# Quickstart (/docs/api/quickstart)
This walkthrough takes you from zero to a working request: get a key, call the API, and read a paginated list.
## Get an API key [#get-an-api-key]
API keys are created by the store owner inside [Allomancy Stores](https://stores.allomancy.com), on the API Keys page. The owner clicks Create API Key, gives the key a name, picks the scopes it should have, and optionally sets an expiry date.
The full key is shown only once, at creation. It looks like this:
```
allo_live_8Kd2...redacted...zQ.Hk9...redacted...4w
```
The owner copies it with Copy and stores it somewhere secure right away. Allomancy stores only a hash, so the full key can never be retrieved again. If it is lost, the owner revokes it and creates a new one. See [Authentication](/docs/api/authentication) for the key format and lifecycle.
## Make your first call [#make-your-first-call]
Send the key in the `X-API-Key` header. Start with `GET /v1/store`, which returns the store the key belongs to:
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/store \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/store",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch("https://integrations.allomancy.net/v1/store", {
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
});
console.log(await response.json());
```
```php
A successful call returns `200 OK` with the store details:
```json
{
"id": "6f9c1b2e-4a7d-4e91-9c3a-2b8d5e0f1a23",
"name": "Aether & Ash",
"logo": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"blogListed": true,
"applicationOpen": false,
"bonus": { "baseBonus": 0, "groupBonus": 5, "creditExpiryDays": null },
"locationSlUrl": "http://maps.secondlife.com/secondlife/Aether/128/64/22",
"ownerUuid": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"ownerUsername": "aether.ash"
}
```
If the key is missing or invalid you get `401`; if it is valid but lacks the `store:read` scope you get `403`. See [Authentication](/docs/api/authentication) for the full status-code model.
## Read a paginated list [#read-a-paginated-list]
List endpoints return a cursor-paginated envelope. Request products, limiting the page to ten rows:
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/products?limit=10" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/products",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 10},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/products?limit=10",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The `data` array holds the rows, and `pagination` carries the cursors:
```json
{
"data": [
{
"id": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"name": "Fancy hat",
"price": 250,
"activePrice": 225,
"image": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"bloggable": true,
"active": true,
"blockCredits": false,
"item": "Fancy hat",
"limited": false,
"availableAmount": 0,
"permissions": { "copy": true, "modify": false, "transfer": true },
"discount": { "value": 10, "type": 0, "startDate": "2026-04-01T00:00:00Z", "endDate": "2026-05-01T00:00:00Z" },
"description": "A very fancy hat.",
"demoId": null,
"mpListing": 4242,
"profitShares": [
{ "userUuid": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e", "percent": 10 }
]
}
],
"pagination": {
"nextCursor": "Tjo3ZDJlNGM4NC05YTZmLTFiNWU4ZDBjMmEzNw",
"previousCursor": null,
"limit": 10
}
}
```
To fetch the next page, send the `nextCursor` value back as the `cursor` query parameter. When `nextCursor` is `null`, there are no more rows. Cursors are opaque tokens: pass them back exactly as received, and do not build or decode them yourself. See [Pagination and errors](/docs/api/pagination-and-errors) for the full envelope and the error format.
# Scopes (/docs/api/scopes)
Scopes control what an API key can do. Each endpoint requires exactly one scope, and a key reaches only the endpoints whose scope it holds. The store owner picks the scopes when creating or editing a key, so grant a key only the scopes its integration needs.
## Available scopes [#available-scopes]
| Scope | Grants |
| --------------------- | --------------------------------------------------------------------------------- |
| `store:read` | Read store details (`GET /v1/store`) and loyalty tiers (`GET /v1/loyalty/tiers`). |
| `products:read` | Read products. |
| `products:write` | Create and update products. |
| `products:deliver` | Deliver a product to a resident. |
| `vendors:read` | Read vendors. |
| `vendors:write` | Update vendors. |
| `customers:read` | Read customers, including their spend. |
| `customers:write` | Update customers, and ban or unban them. |
| `credits:read` | Read a customer's store credit. |
| `credits:write` | Set or adjust a customer's store credit. |
| `customertokens:mint` | Mint a customer access token. |
| `categories:read` | Read categories. |
| `categories:write` | Create, update, and delete categories, and manage their products. |
| `coupons:read` | Read coupons. |
| `coupons:write` | Create and update coupons. |
| `coupons:apply` | Apply a coupon to, or revoke one from, a customer. |
| `giftcards:read` | Read gift cards. |
| `giftcards:write` | Grant, update, and disable gift cards. |
## How scopes map to endpoints [#how-scopes-map-to-endpoints]
A request that authenticates with a valid key but lacks the endpoint's scope returns `403`. The scope each endpoint requires is listed on that endpoint's reference page. Read and write are separate scopes within most areas, so a read-only integration never needs a write scope.
One pairing is worth noting: a single scope can gate endpoints in more than one area. `store:read` covers both the store details endpoint and the loyalty tiers endpoint, because loyalty tiers are part of the store's read surface.
# Store and loyalty (/docs/api/store-and-loyalty)
These two read-only endpoints return the store's own details and its loyalty tiers. Both use the `store:read` scope, so a key with `store:read` reaches both. Field shapes are on the [Object reference](/docs/api/object-reference) page.
## Get store details [#get-store-details]
```
GET /v1/store
```
Returns the store the key belongs to. Scope: `store:read`.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/store \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/store",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch("https://integrations.allomancy.net/v1/store", {
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
});
console.log(await response.json());
```
```php
```json
{
"id": "6f9c1b2e-4a7d-4e91-9c3a-2b8d5e0f1a23",
"name": "Aether & Ash",
"logo": "0d4f8a11-9b2c-4e6d-8f1a-3c7b5d9e2f04",
"blogListed": true,
"applicationOpen": false,
"bonus": { "baseBonus": 0, "groupBonus": 5, "creditExpiryDays": null },
"locationSlUrl": "http://maps.secondlife.com/secondlife/Aether/128/64/22",
"ownerUuid": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"ownerUsername": "aether.ash"
}
```
The response is a [StoreResponse](/docs/api/object-reference#storeresponse). The `locationSlUrl` is a Second Life map URL for the store, or an empty string when no location is set. Status: `200`, or `404` if the store cannot be found.
## Get loyalty tiers [#get-loyalty-tiers]
```
GET /v1/loyalty/tiers
```
Returns the store's loyalty tiers. Scope: `store:read`.
Unlike the list endpoints elsewhere in the API, this one returns a **bare JSON array** of [LoyaltyTierResponse](/docs/api/object-reference#loyaltytierresponse) objects. It is not cursor-paginated and not wrapped in the `data` and `pagination` envelope. A store with no tiers returns an empty array.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/loyalty/tiers \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/loyalty/tiers",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/loyalty/tiers",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
[
{ "spendingThreshold": 1000, "discountPercentage": 5, "isGroupOnly": false, "isLastYearOnly": false },
{ "spendingThreshold": 5000, "discountPercentage": 10, "isGroupOnly": false, "isLastYearOnly": true }
]
```
Status: always `200`.
## Related pages [#related-pages]
# Vendors (/docs/api/vendors)
The vendors endpoints read and update a store's inworld vendors, the units that sell a product to residents. Vendors are owner-scoped: these endpoints see only the vendors your key's store owns, and a vendor id owned by another store returns `404`. Every field shape is on the [Object reference](/docs/api/object-reference) page.
## List vendors [#list-vendors]
```
GET /v1/vendors
```
Returns a cursor-paginated list of the store's vendors. Scope: `vendors:read`.
Query parameters `limit` and `cursor` are optional; see [Pagination and errors](/docs/api/pagination-and-errors) for the envelope and cursor rules.
You can also narrow the list with optional filters:
* `search` (string): return only vendors whose name, or whose linked product's name, contains the term. The match is case-insensitive and partial.
* `region` (string): return only vendors in a Second Life region, matched by exact region name (case-insensitive).
* `minPrice` / `maxPrice` (integer): return only vendors whose price falls within the `L$` range. Vendors that inherit their product's price (a price of `-1`, described under [Update a vendor](#update-a-vendor)) are left out of price filtering.
* `type` (integer): return only vendors of one [VendorType](/docs/api/object-reference#vendortype).
* `minDiscount` (integer): return only vendors whose own discount is at least this percentage. This reads the vendor discount, not the store-group discount.
* `active` (string): tri-state. Omitted or `active` returns active-only vendors (the default), `inactive` returns inactive-only, and `all` returns both. Any other value returns `400`.
Filters combine with AND, so sending several narrows the list to vendors matching all of them. For example, `GET /v1/vendors?type=2&active=all&minDiscount=10&limit=50` returns up to 50 vendors of type `2` (Multivendor), active or not, whose discount is at least 10 percent.
```bash
curl --fail-with-body "https://integrations.allomancy.net/v1/vendors?limit=50" \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/vendors",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
params={"limit": 50},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/vendors?limit=50",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
```json
{
"data": [
{
"id": "5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90",
"name": "Fancy hat vendor",
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"productName": "Fancy hat",
"syncName": true,
"active": true,
"trackable": true,
"price": 250,
"imageFace": 2,
"categoryId": null,
"gachaId": null,
"groupOnly": false,
"discount": 10,
"groupDiscount": 15,
"blockCredits": false,
"clickAction": 2,
"touchToAcquire": false,
"type": 0,
"region": "Fancy Island",
"position": "<128.0, 64.0, 25.0>"
}
],
"pagination": {
"nextCursor": "Tjo1YzJiOWQ3ZS00YTFmLTRlODMtYjZkMC0yZjhhMWMzZTViOTA",
"previousCursor": null,
"limit": 50
}
}
```
Each row is a [VendorResponse](/docs/api/object-reference#vendorresponse). A `price` of `-1` means the vendor inherits its linked product's price. Status: `200`, or `400` for a bad filter or pagination parameter.
## Get a vendor [#get-a-vendor]
```
GET /v1/vendors/{id}
```
Returns one vendor by its UUID. Scope: `vendors:read`.
Path parameter: `id`, the vendor's UUID.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/vendors/5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90 \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w"
```
```python
import requests
response = requests.get(
"https://integrations.allomancy.net/v1/vendors/5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/vendors/5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90",
{
headers: { "X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w" },
},
);
console.log(await response.json());
```
```php
The response is a [VendorResponse](/docs/api/object-reference#vendorresponse). Status: `200`, or `404` if no vendor matches.
## Update a vendor [#update-a-vendor]
```
PUT /v1/vendors/{id}
```
Replaces a vendor's fields. Scope: `vendors:write`. The body is an [UpdateVendorRequest](/docs/api/object-reference#updatevendorrequest), and it replaces the vendor's fields: an omitted field is set to its default rather than left at its previous value.
The `name` you send is applied for most vendor types. The exception is `syncName`: with `syncName` on, a Default vendor takes its name from its linked product and a gacha vendor from its linked gacha, so for those two types the `name` you send is ignored. Send `null` for `price`, or omit it, to have the vendor inherit its linked product's price.
```bash
curl --fail-with-body https://integrations.allomancy.net/v1/vendors/5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90 \
-X PUT \
-H "X-API-Key: allo_live_8Kd2...zQ.Hk9...4w" \
-H "Content-Type: application/json" \
-d '{
"name": "Fancy hat vendor",
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"syncName": false,
"active": true,
"trackable": true,
"price": 250,
"imageFace": 2,
"categoryId": null,
"gachaId": null,
"groupOnly": false,
"discount": 10,
"groupDiscount": 15,
"blockCredits": false,
"clickAction": 2,
"touchToAcquire": false,
"type": 0
}'
```
```python
import requests
response = requests.put(
"https://integrations.allomancy.net/v1/vendors/5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90",
headers={"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w"},
json={
"name": "Fancy hat vendor",
"productId": "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"syncName": False,
"active": True,
"trackable": True,
"price": 250,
"imageFace": 2,
"categoryId": None,
"gachaId": None,
"groupOnly": False,
"discount": 10,
"groupDiscount": 15,
"blockCredits": False,
"clickAction": 2,
"touchToAcquire": False,
"type": 0,
},
)
print(response.json())
```
```js
const response = await fetch(
"https://integrations.allomancy.net/v1/vendors/5c2b9d7e-4a1f-4e83-b6d0-2f8a1c3e5b90",
{
method: "PUT",
headers: {
"X-API-Key": "allo_live_8Kd2...zQ.Hk9...4w",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Fancy hat vendor",
productId: "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
syncName: false,
active: true,
trackable: true,
price: 250,
imageFace: 2,
categoryId: null,
gachaId: null,
groupOnly: false,
discount: 10,
groupDiscount: 15,
blockCredits: false,
clickAction: 2,
touchToAcquire: false,
type: 0,
}),
},
);
console.log(await response.json());
```
```php
"Fancy hat vendor",
"productId" => "3a1f0c9b-7d2e-4c84-9a6f-1b5e8d0c2a37",
"syncName" => false,
"active" => true,
"trackable" => true,
"price" => 250,
"imageFace" => 2,
"categoryId" => null,
"gachaId" => null,
"groupOnly" => false,
"discount" => 10,
"groupDiscount" => 15,
"blockCredits" => false,
"clickAction" => 2,
"touchToAcquire" => false,
"type" => 0,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```
The response is the updated [VendorResponse](/docs/api/object-reference#vendorresponse). Status: `200`; `404` if no vendor matches; or `400` for an invalid body. A `400` also covers the field rules: `discount` and `groupDiscount` must each be 0 to 100, and `groupDiscount` must be at least `discount`; `imageFace` must be 0 to 7; `name` must be at most 63 characters; `price` must be `-1` (inherit) or 0 or greater; `type` and `clickAction` must be recognized values; a `type` of MultiMenuVendor requires a `categoryId` and a `type` of GachaVendor requires a `gachaId`; and a `productId`, `categoryId`, or `gachaId` that does not belong to the store is rejected (`Product not found`, `Category not found`, `Gacha not found`).
## Related pages [#related-pages]
# Account Management (/docs/features/account-management)
Account Management covers your signed-in Allomancy account and the Second Life avatar linked to it. For first-time signup, email verification, and the initial inworld package, start with [Account Creation](/docs/quickstart/account-creation).
## Where to find My Account [#where-to-find-my-account]
Open the avatar menu in the top header and choose **My Account**. This opens [stores.allomancy.com/account](https://stores.allomancy.com/account).
## Reviewing your account information [#reviewing-your-account-information]
The **Account Information** card shows your Second Life profile image, **Second Life Username**, and **Email Address**.
## Unlinking your Second Life profile [#unlinking-your-second-life-profile]
On the **Account Information** card, click Unlink SL Profile to unlink the avatar from your Allomancy account.
After unlinking succeeds, you will need to link a Second Life profile again before using Allomancy normally.
## Linking a profile again [#linking-a-profile-again]
If your account needs a Second Life profile, Allomancy sends you to the [profile linking page](https://stores.allomancy.com/account/generate-token). Enter your **SL Username** and submit the form to send an inworld confirmation link to your avatar.
Open the link from Second Life local chat to associate that avatar with your Allomancy account. If the link succeeds, you return to the **My Account** page.
For the first-time version of this flow, see [Account Creation](/docs/quickstart/account-creation).
## Signing out [#signing-out]
Open the avatar menu in the top header and choose **Logout** to sign out of Allomancy.
## Permissions [#permissions]
My Account is your own signed-in account page. It is not controlled by Store Manager permissions, and it is separate from store-level settings, staff permissions, and subscriptions.
## Related pages [#related-pages]
# Audit Log (/docs/features/audit-log)
The Audit Log is a running record of actions taken in your store: who acted (a signed-in user, a third-party API key, or the Allomancy system), what they did, and what it affected. Open the Settings page at [stores.allomancy.com/settings](https://stores.allomancy.com/settings) and click the **Audit Log** tile to open it at [stores.allomancy.com/settings/audit-log](https://stores.allomancy.com/settings/audit-log).
## Reading the log [#reading-the-log]
Each row shows:
* **Time**: how long ago the event happened. Hover for the exact date and time in your local time.
* **Actor**: a **User**, **API Key**, or **System** badge plus the actor's name.
* **Action**: the action's name, such as `Product.Update`.
* **Description**: a plain-language summary of what happened.
* **Target**: the type and ID of the item the action affected.
To inspect an event's details, click at the end of its row. The expanded **Metadata** block shows the event's recorded details; Copy copies them, and Filter to this actor narrows the log to everything that user or API key did.
## Searching and filtering [#searching-and-filtering]
Search across descriptions, actors, and actions with the search field, or narrow the log with the filters:
* **Action type**: a specific action name, such as `Product.Update`.
* **Entity type**: the kind of item, such as `Product`.
* **Entity ID**: an exact item ID.
* **Actor type**: the **User** / **API Key** / **System** toggle.
* **Date range**: a start and end date.
Active filters appear as chips above the table; remove one with its X, or click Clear all to reset everything.
Click Load more at the bottom of the table to fetch more events.
## Permissions [#permissions]
Audit Log is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
# Blogger Management (/docs/features/blogger-management)
Allomancy includes a built-in blogger program so you can recruit Second Life bloggers, give them access to your products, and review their posts without leaving the web. The system spans two surfaces: a merchant-facing **Blog Dashboard** inside Allomancy Stores, and a separate blogger-facing website where bloggers apply, retrieve products, submit posts, and coordinate with you.
This page is the starting point for understanding how those pieces fit together. Each major workflow has its own detailed tools inside the Blog Dashboard.
## Where to find Blogger Management [#where-to-find-blogger-management]
The **Blog Dashboard** for the blogger program lives at [stores.allomancy.com/blog](https://stores.allomancy.com/blog).
The Blog Dashboard groups the program into ten tiles:
* **Bloggers**: your active bloggers, with per-blogger profiles and actions.
* **Posts**: posts your bloggers have submitted, with approval and rejection.
* **View Deadlines**: per-blogger and per-product deadlines for posts.
* **Extension requests**: deadline-extension requests from bloggers.
* **Coupons**: coupon codes and the rules bloggers use when they generate their own.
* **Applications**: applications submitted from the blogger-facing site.
* **Messages**: messages sent between you and your bloggers.
* **Vacation requests**: time-off requests from bloggers.
* **Bans**: bloggers blocked from your blogger program.
* **Settings**: store-wide settings, including default deadlines, required posts, onboarding, and the application form.
The Blog Dashboard also shows a link to the blogger-facing website at [bloggers.allomancy.com](https://bloggers.allomancy.com).
## Opening your blogger program [#opening-your-blogger-program]
Two switches on the Blog Dashboard control whether new bloggers can find and apply to your store:
* **Applications are open / closed**: when open, bloggers can submit an application to your store. The label appears in green when applications are open and in red when they are closed.
* **Listed for bloggers**: when enabled, your store is listed on [bloggers.allomancy.com](https://bloggers.allomancy.com) so bloggers can discover it. This switch is disabled while applications are closed.
While applications are open, the Blog Dashboard also shows a copyable **Applications URL** for your store on `bloggers.allomancy.com`. You can share this link directly so a specific blogger lands on your application form without browsing the public list.
## The blogger-facing website [#the-blogger-facing-website]
Bloggers do their work on the separate site at [bloggers.allomancy.com](https://bloggers.allomancy.com). From there they can:
* Find and apply to stores that have applications open.
* Manage their blogger profile, including a short bio and links to their blog and social pages.
* Track which stores they currently blog for.
* Browse and select retrievable products from a store and submit a post about them.
* Update an existing post that is still pending review.
* Request a deadline extension for products they have selected.
* Request time off from a store with a vacation request.
* Read messages sent by the merchant.
* Generate or update their personal coupon code, when the merchant has enabled blogger coupons.
* Leave a store they no longer want to blog for.
You do not access the blogger site as a merchant. Bloggers reach it themselves, log in with their own account, and pair their Second Life username through that site's account flow.
## The merchant lifecycle [#the-merchant-lifecycle]
A typical blogger relationship moves through these stages from the merchant side:
### Open applications [#open-applications]
On the Blog Dashboard, turn on **Applications are open** and, if you want bloggers to discover your store on the public list, **Listed for bloggers**. Configure your application questions and the rest of your program in the **Settings** tile before opening, so applicants see the form you want.
### Review applications [#review-applications]
When a blogger applies from [bloggers.allomancy.com](https://bloggers.allomancy.com), their application appears in the **Applications** tile. Open each application to read the answers and accept or reject the applicant.
### Manage your bloggers [#manage-your-bloggers]
Accepted bloggers appear in the **Bloggers** tile. From there you can review per-blogger profiles, retrieved products, and post counts, and take per-blogger actions when needed.
### Track posts and deadlines [#track-posts-and-deadlines]
Use the **Posts** tile to review and approve or reject submitted posts, and the **View Deadlines** and **Extension requests** tiles to track deadlines and respond to extension requests.
### Coordinate, reward, and moderate [#coordinate-reward-and-moderate]
Use **Messages** to communicate with your bloggers, **Coupons** to manage blogger coupon codes and store-wide coupon settings, **Vacation requests** to handle time-off, and **Bans** to block bloggers from your program when needed.
## Permissions [#permissions]
Access to the Blog Dashboard is gated by the **Manage Bloggers** permission. Store owners always have access. Store managers see the Blog item in the sidebar only when their permissions include Manage Bloggers. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers).
The **Bans** tile under the Blog Dashboard is for bloggers in your blogger program. It is separate from the customer ban list under Settings, which blocks a customer from purchasing.
## Related pages [#related-pages]
# Customer Bans (/docs/features/customer-bans)
When you need to block a specific Second Life resident from buying anything in your store, the Customer Bans page is where you create the ban, review who is currently blocked and why, and lift the ban when appropriate.
This page covers the **Bans** tile on the Settings page.
## Where to find Customer Bans [#where-to-find-customer-bans]
Open the Settings page at [stores.allomancy.com/settings](https://stores.allomancy.com/settings) and click the **Bans** tile to open the **Banned Users** page at [stores.allomancy.com/settings/bans](https://stores.allomancy.com/settings/bans).
## The banned users list [#the-banned-users-list]
The list shows every customer currently banned from your store. Each row has the following columns:
* **Username**: the resident's Second Life profile picture and username.
* **Reason**: the reason you entered when you created the ban, truncated for the table.
* **Ban Date**: the date and time the ban was created, formatted like `Apr 27, 2026 3:42 pm`.
* **Actions**: an unban button described in [Lifting a ban](#lifting-a-ban) below.
When a reason is longer than what fits in the column, click next to the truncated text to open a **Ban Reason for {'{username}'}** dialog showing the full reason in a quoted block.
## Banning a customer [#banning-a-customer]
To ban a customer, click Ban User at the top of the page, enter the resident's Second Life **Username**, optionally add a **Reason**, and click Ban User
The reason you enter appears on the Bans page. Customers do not see the reason. When a banned customer tries to make a purchase, Allomancy refunds their L$ and sends them a local-chat message stating they are banned from the store.
## Lifting a ban [#lifting-a-ban]
In the **Actions** column, click to unban the customer.
While a customer is banned, they cannot purchase from your store, buy products as gifts, or request redeliveries. The ban does not delete their past purchases, sales history, or customer profile.
## Permissions [#permissions]
Customer Bans is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Customer Import (/docs/features/customer-import)
Customer Import is the flow for bringing existing customer records into Allomancy from a CSV file. Use it when migrating from another system, restoring historical customer data, or preserving known customer details during a migration.
## Where to find Customer Import [#where-to-find-customer-import]
On the [Customers page](/docs/features/customers), click at the top to open the **Import Customers** flow at [stores.allomancy.com/customers/import](https://stores.allomancy.com/customers/import).
## CSV format [#csv-format]
Allomancy matches each CSV column name against familiar words, regardless of capitalization. The first matching rule below wins:
| If a column header contains | The value is read as |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `key` or `uuid` | the customer's UUID |
| `name` or `customer` | the customer's username |
| `gift card credits` or `gift_card_credits` | added into the customer's store credit |
| `restrictedcredit` | the customer's restricted credit |
| `credit` (any other credit-named column) | the customer's store credit |
| `discount` | the customer's per-customer discount, as a whole-number percentage |
| `subscriber` | the News Subscriber flag, with `true`, `1`, or `yes` (case-insensitive) interpreted as subscribed |
A row is included only if at least one of the username and UUID columns has a value. Headers Allomancy does not recognize are silently skipped.
If a row has both a regular credit column and a gift card credits column, the two amounts are added together and stored as the customer's credit balance. The restricted credit is kept separate.
## Running an import [#running-an-import]
### Open the import page [#open-the-import-page]
Click on the Customers page to open the import wizard.
### Drop or pick a CSV file [#drop-or-pick-a-csv-file]
Drag a CSV file onto the **1. Upload the CSV File** dropzone or click the dropzone to select a file. Allomancy reads the file. If no valid customer data is found, the dropzone asks you to ensure the CSV includes the required customer columns.
### Review the preview [#review-the-preview]
After Allomancy reads the file, the page shows **2. Preview Customer Data** with a table of the rows it found. The table includes columns for Customer, Credit, Discount, and News Subscriber. To flip the subscriber flag on all imported rows at once, click Toggle News Subscription
Toggling **News Subscription** on for imported customers without their consent could be seen as spam.
### Import [#import]
Click **Import *N* Customers** at the bottom of the preview. Rows that have no credit, no restricted credit, no discount, and are not subscribed are skipped.
### Wait for completion [#wait-for-completion]
Large imports may keep processing in the background for a few minutes. To start over with a different CSV, click Import New File
## How imports relate to other features [#how-imports-relate-to-other-features]
* **Per-customer credit and discount**: the values from the import end up on each customer's profile, on the same **Credit**, **Restricted Credit**, and **Discount** fields you can edit on the [Customers page](/docs/features/customers).
* **Loyalty discounts**: customer import does not change your store's loyalty tier rules and does not retroactively populate historical spending totals. Loyalty tiers continue to apply based on actual spending after the import.
* **News Kiosk subscriber**: a `subscriber` column or the Toggle News Subscription button sets the same flag shown as **News Subscriber** on the customer list and on the [News Kiosk](/docs/features/news-kiosk) subscribers page.
* **Different from the News Subscriber import**: the News Subscriber import recognizes only the name and UUID columns and treats every imported row as a subscriber. Customer Import supports additional customer-record fields, such as credit, restricted credit, and per-customer discount, when those values already exist in the records you are migrating.
## Permissions [#permissions]
Customer import is a write action on customer data, so it needs the **Manage Customers** permission. Store owners always have it. To grant it to a staff member, see [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Customer Page (/docs/features/customer-page)
Your customer page is the website where your customers open redelivery links and manage their gift cards. By default, customers use the **customer.allomancy.com** page. The Customer Page settings let you host your own branded page on your domain instead, and let trusted stores band together into a **collective** that shares a single page.
Open the Settings page at [stores.allomancy.com/settings](https://stores.allomancy.com/settings) and click the **Customer Page** tile. The page has two tabs, **Your customer page** and **Collective**, with an **Invitations** section below them.
## Hosting your own customer page [#hosting-your-own-customer-page]
To point your customers at your own branded page, enter a **Customer page origin** on the **Your customer page** tab (an `https://` origin with no path, query, or credentials) and click Save
From then on, your customers' redelivery links open on that domain. The open-source [allomancy-customer-page](https://github.com/AllomancySystem/allomancy-customer-page) example app is a fork-and-restyle reference you can deploy to your own domain.
To go back to the default **customer.allomancy.com** page, click Clear
## Collectives: one page for several stores [#collectives-one-page-for-several-stores]
A collective is one shared customer page, owned by a single merchant. When your store joins, your customers can see their transactions, gift cards, and products from across the collective on one page.
The collective's owner runs the shared page and can reach the customer pages of every member store, including their gift cards. Only join a collective whose owner you trust. Leaving a collective stops the sharing immediately.
While your store is in a collective, the **Your customer page** tab is locked: leave the collective to manage your own customer-page domain.
### Creating a collective [#creating-a-collective]
On the **Collective** tab, enter a **Collective name** and optionally a **Logo texture UUID**; together they are the brand your customers see on the shared page. The **Collective page origin** is the custom domain that hosts the shared page, and until one is set, member stores' redelivery links stay on each store's own page. You can set the origin now or later from the same form. To create the collective, click Create collective
### Inviting a store [#inviting-a-store]
To invite another merchant's store, click Invite a store next to **Member stores**, enter the **Store owner's username**, and click Send invitation
Allomancy looks up that username and shows you the avatar's profile picture, so you can confirm it matches the merchant you meant before the invitation goes out. The invited owner must accept before their store becomes a member; until then, the store shows as **Invited** in the member list.
### Joining a collective you were invited to [#joining-a-collective-you-were-invited-to]
Collectives that have invited your store appear under **Invitations**. To review what joining shares before you commit, click Review and join to open the dialog. There, Join collective stays locked until you tick the checkbox confirming you trust the collective's owner. Declining removes the invitation, though the owner can invite your store again later; to turn it down, click Decline
### Choosing which page your customers see [#choosing-which-page-your-customers-see]
Once your store is in a collective, the **Use collective page** switch on the **Shared collective page** card chooses whether your customers' redelivery links open the shared collective page or your own store's page. The switch affects only your store; every member store chooses for itself.
### Removing a store, leaving, and disbanding [#removing-a-store-leaving-and-disbanding]
As the collective's owner, click Remove next to a member store to remove just that store; the collective stays active for everyone else. The removed store loses access to the shared page and can no longer redeem or transfer the shared customers' gift cards.
As a member, click Leave collective to exit. The collective continues without your store: your customers stop being shared, and you lose access to the shared page.
As the owner, click Disband collective to end the whole collective.
Disbanding ends the collective for every store: the shared page stops working, every member store is removed, and any active customer links pointing at the collective stop resolving. This cannot be undone.
## Permissions [#permissions]
Customer Page is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Customers (/docs/features/customers)
The Customers page lists everyone who has bought from your store. From here you can review what each customer has spent, look at their purchase history, edit their store credit, run bulk credit adjustments, and configure store-wide loyalty discounts.
The customers list lives at [stores.allomancy.com/customers](https://stores.allomancy.com/customers).
## Viewing the customer list [#viewing-the-customer-list]
Each row in the list shows:
* **Customer**: the resident's username.
* **Spent**: lifetime L$ spent in your store. The list is sorted by this column by default, highest first.
* **Spent -1Y**: L$ spent in the last year.
* **Owns**: number of products this customer has bought.
* **Credit**: store credit balance the customer can spend at any of your vendors.
* **Restricted Credit**: store credit the customer can spend on themselves but cannot use for gifting.
* **Discount**: a per-customer discount percentage you have set manually.
* **News Subscriber**: whether the customer is subscribed to your store [news kiosk](/docs/features/news-kiosk).
Click any column header to sort the list by that field.
## Managing a customer [#managing-a-customer]
To manage a customer, click in the **Action** column of their row. The customer drawer opens with the customer's Second Life profile picture, their **Store credit** and **Restricted credit** balances, and two tabs: **Info** and **Credits**.
On the **Info** tab, set the customer's **Discount (%)**, a per-customer discount assigned directly to this resident; to apply it, click Save
Per-customer discount is independent of the loyalty tiers described below. Use it for personal gestures (a thank-you, a one-off comp), and use loyalty discounts for store-wide spend-based rules.
Below the discount, the **Owned products** list shows everything the customer has bought from you; long lists get a search field. For each product:
* To redeliver that product to this customer, click
* To remove the product from their purchases and stop future redeliveries of it, click
The **Credits** tab holds the customer's store credit: the never-expiring balances, expiring credit lots, and the credit history. See [Store Credit](/docs/features/store-credit) for the full guide.
To add a customer manually, click at the top of the list. Enter their Second Life username, set their starting credits and discount, and save. Each credit field carries optional **Expiry** presets, so starting credit can be granted with an expiry date; [Store Credit](/docs/features/store-credit) explains how expiring credit behaves.
## Adjusting credits in bulk [#adjusting-credits-in-bulk]
The button at the top of the page opens a dialog that adjusts credits for many customers at once. The values you enter are applied as a *change* to each customer's existing credits, not as a new total. Positive numbers add credits, negative numbers remove them.
### Optionally narrow the audience [#optionally-narrow-the-audience]
Turn on **Filter by customers spent amount** to limit the change to customers who have spent more than, or less than, a given L$ amount. Without a filter, every customer in your store is updated.
### Enter the credit changes [#enter-the-credit-changes]
Set **Credits change (L$)** for unrestricted credits and **Restricted Credits change (L$)** for restricted credits. Either or both can be positive, negative, or zero. Optionally set an **Expiry (applies to added credit)**: the expiry applies only when you are adding credit, not when you are reducing it. [Store Credit](/docs/features/store-credit) explains how expiring credit behaves.
### Confirm [#confirm]
With no filter set, the update touches every customer in your store. To run the update, click Update
## Loyalty discounts [#loyalty-discounts]
Loyalty discounts are store-wide rules that reward customers based on how much they have spent. Unlike the per-customer discount, you do not assign loyalty discounts manually to each resident. Allomancy matches each customer against the configured tiers and uses the highest tier they qualify for.
Open from the Customers page to manage your tiers.
### How a tier works [#how-a-tier-works]
Each tier has four settings:
* **Spending threshold (L$)**: the minimum total a customer must have spent to qualify for the tier.
* **Period**: whether the threshold is checked against **Global** spending (the customer's lifetime total at your store) or **Yearly** spending (their total over the last year).
* **Discount %**: the percentage discount the tier grants when the customer qualifies.
* **Group Only**: when enabled, only customers who are in your store group qualify for this tier.
A customer is matched against every tier and receives the discount from the highest threshold they meet. Two tiers cannot share the same period and threshold; the form rejects duplicates when you save.
### Adding and removing tiers [#adding-and-removing-tiers]
### Open the loyalty editor [#open-the-loyalty-editor]
Click on the Customers page. The dialog shows your existing tiers, or one empty row if you have not configured any.
### Add a tier [#add-a-tier]
Click Add New Tier and enter the spending threshold in L$, choose **Global** or **Yearly** for the period, set the discount percentage, and tick **Group Only** if the tier should be restricted.
### Remove a tier [#remove-a-tier]
Click at the right edge of any row to delete that tier.
### Save [#save]
Empty tiers (zero threshold and zero discount) are discarded automatically, and the remaining tiers are sorted by threshold for you. To save the tier list, click Save Changes
### Global vs Yearly periods [#global-vs-yearly-periods]
* **Global** uses lifetime spending. Once a customer earns into a Global tier, they keep it as long as the tier exists.
* **Yearly** uses last-year spending, matching the *Spent -1Y* column on the customer list. Customers can drop out of a Yearly tier if their recent spending falls below the threshold.
You can mix periods in the same tier ladder, for example a permanent reward for big lifetime spenders alongside a recurring reward for high recent activity.
### How loyalty relates to per-customer discount [#how-loyalty-relates-to-per-customer-discount]
The per-customer **Discount %** field on a customer's profile and the loyalty tiers are configured separately. Loyalty applies automatically based on spending; per-customer discount applies because you set it on that customer's profile.
Discounts apply in a fixed order at purchase time:
1. **Product discount** (if any) reduces the product's listed price.
2. **Vendor discount** or **vendor group discount** is applied to that result, giving the price the customer pays at the vendor inworld.
3. After payment, the customer's **per-customer discount** OR **loyalty tier discount** (whichever is larger) is refunded back to the customer.
4. If a **blogger coupon** was used, the coupon's customer-discount portion is added to the customer's store credit balance.
Categories are not a separate discount type. Setting a discount on a category applies the same product discount to every product in the category, replacing any existing product-level discount on those products.
L$ amounts are integers; any calculation that produces a fractional L$ is rounded down.
**Example.** A product priced at L$100 with a 10% product discount becomes L$90. The vendor selling it has a 5% vendor discount, so L$4 (5% of L$90, rounded down from L$4.50) is deducted: the customer pays L$86 at the vendor inworld. The customer also has a 5% per-customer discount, so L$4 (5% of L$86, rounded down from L$4.30) is refunded back to them. Net cost: L$82.
## Importing customers [#importing-customers]
You can bulk-import customers from a CSV file using the upload button at the top of the Customers page. For the full migration flow, including supported credit, discount, and subscriber columns, see [Customer Import](/docs/features/customer-import).
## Related pages [#related-pages]
# Discord Notifications (/docs/features/discord-notifications)
Discord notifications let your team see sales as they happen. Add a webhook once, choose who can see that Discord channel, and Allomancy can post sale activity there automatically.
## What gets sent [#what-gets-sent]
Each notification includes the transaction details your staff needs, such as the product, buyer, and amount.
## Setting up a webhook [#setting-up-a-webhook]
### Create a webhook in Discord [#create-a-webhook-in-discord]
In your Discord server, right-click the text channel that should receive notifications and choose **Edit Channel**. Open **Integrations**, click **Create Webhook**, then copy the generated webhook URL.
### Add the webhook to Allomancy [#add-the-webhook-to-allomancy]
On [Allomancy Stores](https://stores.allomancy.com), open the [Discord Webhooks page](https://stores.allomancy.com/settings/discord-webhooks) and click at the top. Enter a recognizable label in the **Name** field, paste the URL into the **Webhook** field, then click Save
### Send a sample notification [#send-a-sample-notification]
The first webhook you add becomes your store's default webhook automatically. Make a small purchase inworld so the first notification lands in the chosen Discord channel.
## Choosing which sales get sent [#choosing-which-sales-get-sent]
The **Extra notifications** dialog on [Store Settings](/docs/features/store-settings) has the Discord controls:
* **Inworld sales (Discord)**: pick **Non free** to send only sales that cost something, or **All** to send every inworld sale.
* **Marketplace sales (Discord)**: the same choice for your Second Life Marketplace sales.
* **Payment Terminal payments (Discord)**: tick to receive a notification when a [Payment Terminal](/docs/features/vendors) payment is made.
# Gachas (/docs/features/gachas)
A gacha is a randomized prize pool that an inworld Gacha vendor pulls from. Configure the price each pull costs, the chance of pulling a rare item, and an optional guarantee counter that can award a rare item after a number of consecutive non-rare pulls.
The Gachas page lives at [stores.allomancy.com/gachas](https://stores.allomancy.com/gachas).
## Creating a gacha [#creating-a-gacha]
To create the gacha and open its detail page for configuration, enter a name in **New gacha name** (a label for your own organization) and click Add Gacha
## Configuring a gacha [#configuring-a-gacha]
This gacha's detail page is where you set the price, rarity, and guarantee behavior (pity counter), and where you choose which products the gacha hands out.
To rename a gacha, hover the title at the top of the page and click it. The header turns into an input. Press **Enter** to save or **Esc** to cancel.
The **Gacha Settings** card has three numeric fields:
* **Price (L$)**: how much each pull costs.
* **Rare chance (%)**: percentage chance that any single pull returns a rare item.
* **Guarantee At**: optional guarantee counter. After this many consecutive non-rare pulls in a row, the next pull is guaranteed to return a rare item. **Set to 0 to disable the guarantee counter.**
To apply changes, click Save Settings
## Choosing which products go in the gacha [#choosing-which-products-go-in-the-gacha]
Below the settings, a two-column board shows your existing products:
* **Other products**: products that are not currently part of this gacha.
* **In Gacha**: products that the gacha can pull from.
Drag a product between columns to add or remove it.
If you have many products to move at once, use the **Move** and **Remove** buttons above the board to shift the entire list in either direction.
Make sure the products you add to the gacha have permissions set properly. The detail page shows this reminder, because the gacha hands out the items as if they were sold normally.
## How items are marked as rare [#how-items-are-marked-as-rare]
A gacha tells rare items apart from common items by looking at the product or item name. To flag a product as rare, include one of these patterns in its name:
* `[rare]` (case-insensitive)
* `(rare)` (case-insensitive)
* `{rare}` (case-insensitive)
* `RARE` (must be in ALL CAPS)
A pull either returns a rare item (when the rare-chance roll wins, or when the guarantee counter is reached) or a non-rare item from the rest of the pool.
## Pairing a gacha with a vendor [#pairing-a-gacha-with-a-vendor]
A gacha by itself does not sell anything. Pair it with an inworld vendor through the [Vendors](/docs/features/vendors) page:
1. Open the vendor's edit dialog and go to the **Vendor Type** step.
2. Select **Gacha** as the vendor type.
3. Use the **Gacha Selection** picker to choose this gacha. A shortcut next to the picker takes you back to the Gachas page if you need to create a new gacha.
The Vendor Type step needs vendor script version 1.20 or later. Older vendors show a "Please update your vendors" notice in that step.
## Where gacha purchases show up in Sales [#where-gacha-purchases-show-up-in-sales]
Gacha sales appear on the [Sales](/docs/features/sales) tabs alongside regular sales. On the **Sales Table** tab, gacha purchases get a small dice icon in the **Info** column. On the **Products chart**, gacha purchases are merged into the chart when the **Source** filter includes System sales.
## Editing or deleting a gacha [#editing-or-deleting-a-gacha]
Each row on the Gachas list has two action buttons:
* Click to open the gacha's detail page so you can change settings or item membership.
* To delete the gacha, click
## Permissions [#permissions]
The Gachas page is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Gift Card Delivery Units (/docs/features/gift-card-delivery-units)
A Gift Card Delivery Unit is an inworld object that hands out free gift cards to customers, typically for events and promotions. Unlike a [manually issued gift card](/docs/features/gift-cards), which targets one specific resident at a time, a Delivery Unit lets customers pick up a card from the inworld object.
Delivery Units live at [stores.allomancy.com/gift-cards/delivery-units](https://stores.allomancy.com/gift-cards/delivery-units). The page handles configuration and produces an ID that you paste into the inworld object to pair the two together.
## Creating a Delivery Unit [#creating-a-delivery-unit]
On the **Gift-cards Delivery Units** page, click New unit at the top of the list. Fill in the fields described below, then click Create
### Field reference [#field-reference]
* **Unit name**: a label for your own organization.
* **Credit**: the L$ value of each gift card the unit hands out. Default L$100.
* **Custom texture**: an optional Second Life texture UUID. When left blank, the card uses your store logo. See [What image appears on the gift card](#what-image-appears-on-the-gift-card) below.
* **Group Only**: marks the unit as group-restricted.
* **Start** and **End**: optional date range for the unit. Leave them empty for an open-ended unit.
* **Card expiry (days)**: how many days each card stays valid after the unit hands it out; the placeholder reads **Never**, and leaving the field empty means the cards never expire. This is distinct from **Start** and **End**, which gate when the unit dispenses.
## Setting up the unit inworld [#setting-up-the-unit-inworld]
After creating the unit, pair it with the inworld Gift Card Delivery Unit item.
### Rez the Delivery Unit [#rez-the-delivery-unit]
Place the Gift Card Delivery Unit object inworld where you want it.
### Copy the Delivery Unit ID [#copy-the-delivery-unit-id]
Back on the Delivery Units page, click the **Copy ID** button on the row for the unit you just created.
### Paste it into the inworld object's description [#paste-it-into-the-inworld-objects-description]
Open the inworld object's edit window and paste the ID into its **Description** field.
## How a unit hands out cards [#how-a-unit-hands-out-cards]
A Delivery Unit hands out **one gift card per customer** by tracking claims against the unit ID in the inworld object's description. Reusing the same ID keeps the same one-card-per-customer campaign.
To start a new round where every customer can claim again, create a fresh Delivery Unit, copy its new ID, and paste that new ID over the old one in the inworld object's **Description** field.
## The two inworld meshes [#the-two-inworld-meshes]
The Gift Card Delivery Unit ships in two meshes:
* a **compact** option, and
* a **terminal** option.
Use whichever fits your store layout. Both use the Delivery Unit ID in the object's description field.
## What image appears on the gift card [#what-image-appears-on-the-gift-card]
If your store has a logo registered on your [Store Settings page](https://stores.allomancy.com/settings/store), gift cards from the unit automatically inherit that logo when no Custom texture is set. To override the store logo for a specific unit, paste a Second Life texture UUID into the **Custom texture** field when you create or edit the unit.
## Editing or deleting a unit [#editing-or-deleting-a-unit]
The Delivery Units list shows every unit you have created. The **Group only** column uses a green when the unit is restricted to your store group, and a red when anyone can use it.
To change a unit's settings, click on its row to reopen the dialog, change what you need, and click Save
To remove a unit, click on its row.
## Permissions [#permissions]
Managing Delivery Units needs the **Manage Gift Cards** permission. Store owners always have it. To grant it to a staff member, see [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Gift Cards (/docs/features/gift-cards)
Gift cards are a quick way to put store credit into a customer's hands. Use them as launch promos, blogger thank-yous, customer-service comps, or pre-paid credit a customer can pass to someone else when transfer is allowed.
The Gift Cards area lives at [stores.allomancy.com/gift-cards](https://stores.allomancy.com/gift-cards). The landing screen has three tiles:
* **Customers cards**: issue, edit, and review every gift card owned by a customer.
* **Redeem history**: see when each gift card was redeemed.
* **Delivery units**: set up inworld dispensers that hand out gift cards. See [Gift Card Delivery Units](/docs/features/gift-card-delivery-units).
## Giving a gift card [#giving-a-gift-card]
Open the **Customers cards** tile and click Send Gift Card at the top of the page to open the **Give a gift card** dialog.
### Enter the recipient [#enter-the-recipient]
Type the recipient's Second Life **username**.
### Choose the credit amount [#choose-the-credit-amount]
Set the **Credit** value in L$.
### Decide whether the card is transferable [#decide-whether-the-card-is-transferable]
Tick **Transferable** if the recipient should be able to pass the card on to someone else. Leave it off to bind the card to the recipient.
### Optionally set an expiry [#optionally-set-an-expiry]
Pick an **Expiry** if the card should only stay valid for a limited time: **30d**, **60d**, **90d**, **1 year**, or **Custom…**, which takes a number of days. Leave **No expiry** selected for a card that never expires.
### Optionally set a custom texture [#optionally-set-a-custom-texture]
Paste a Second Life texture UUID into **Custom texture (optional)** if you want the card to use a custom texture.
### Send [#send]
To create the gift card for that resident, click Send
## Selling gift cards inworld [#selling-gift-cards-inworld]
Customers can also buy their own store credit inworld, at any value they choose, to top up for a purchase or to gift someone.
To set this up, rez the **Allomancy - Customer & Gift Card Terminal** from your Allomancy system box, the same box that holds your dispenser and vendor, and grant debit permission when Second Life asks. That is the whole setup.
Once the terminal is rezzed, customers pay any L$ amount into its **"Choose amount"** field to buy a gift card of that value. They receive a link in Second Life local chat to their gift cards on the Allomancy customer website. There they redeem the card for store credit or transfer it to someone, the same as a gift card you send from the web.
To hand out free, fixed-value cards you set up in advance instead, see [Gift Card Delivery Units](/docs/features/gift-card-delivery-units).
## Reviewing customer gift cards [#reviewing-customer-gift-cards]
The **Customer Gift Cards** page lists every gift card you have issued. Use the search box at the top to filter by owner.
Each row shows:
* **Owner**: the resident who holds the card.
* **Credit**: the L$ value of the card.
* **Expires**: the card's expiry date, or a dash for cards that never expire. A **Expires soon** badge joins the date when it is within 30 days (turning red at 7 days or fewer), and a **Expired** badge replaces it once the date has passed. Expired cards stay in the list.
* **Transferable**: a green when the card can be transferred, a red when it cannot.
* **Status**: the status column shows Active for active cards and Disabled for disabled cards. Click it to toggle the card on or off.
* **Creation** and **Last change**: when the card was first created and when it was last modified.
* **Action**: click to open the edit dialog for that card.
### Disabling and re-enabling a card [#disabling-and-re-enabling-a-card]
The status button in the Status column is a toggle. Click the button on a row to flip its state.
### Editing a card [#editing-a-card]
To edit a card, click on its row. You can change the credit amount, the **Transferable** flag, and the texture, and flip the **Active** checkbox at the top of the dialog. The username is read-only, and a card's expiry is set when you send it and cannot be changed afterwards. To save, click Update
## Reviewing redemption history [#reviewing-redemption-history]
The **Gift Card Redemption History** page lists every gift card that has been redeemed. Each row shows:
* **Customer**: the resident who redeemed the card.
* **Credit**: the L$ amount redeemed.
* **Date**: when the redemption happened.
## Gift cards and store credit [#gift-cards-and-store-credit]
A gift card can be redeemed for store credit; the redeemed value arrives as a credit lot marked with a gift-card badge, described in [Store Credit](/docs/features/store-credit). Use the [Customers page](/docs/features/customers) to review each customer's credit balances and other customer-specific details.
## Permissions [#permissions]
Managing gift cards needs the **Manage Gift Cards** permission. Store owners always have it. To grant it to a staff member, see [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Home (/docs/features/home)
The **Home** entry in the sidebar opens your store overview: sales totals for a chosen period, the revenue trend, where your sales come from, your best sellers, and a panel listing anything that needs action. The header shows which store's numbers you are looking at; when you are managing another merchant's store, the store name carries a **(manager)** tag.
## Choosing the period [#choosing-the-period]
The **7d / 30d / 90d / 12mo** control at the top scopes everything on the page; **30d** is the default. The trend is grouped by day for **7d** and **30d**, by week for **90d**, and by month for **12mo**.
## Sales at a glance [#sales-at-a-glance]
The tile row summarizes the selected period: **Gross sales**, **Net income**, **Sales count**, and **Average sale**, each with the change compared to the previous period. A **New customers** tile appears alongside them when your permissions include the Customers list.
## Reading the trend chart [#reading-the-trend-chart]
The chart plots one metric at a time; switch between **Revenue** and **Sales count** with the toggle on the card. Hover any point for the gross, net, and sales count of that day, week, or month.
## Sales channel [#sales-channel]
The **Sales channel** card splits the period's revenue between **Inworld** (sales through your vendors inworld) and **Marketplace** (Second Life Marketplace sales), with a bar showing each side's share.
## Top products [#top-products]
**Top products** lists your five best sellers for the period, ranked by gross revenue.
## Needs attention [#needs-attention]
The **Needs attention** panel lists only what currently needs action; when nothing does, it shows an all-clear state instead. The rows you may see:
* **Failed deliveries**: how many inworld deliveries are still failing; a delivery that later succeeds drops out of the count.
* **Blogger applications to review**: pending [applications](/docs/features/bloggers/applications) from bloggers.
* **Vacation requests to review**: pending [vacation requests](/docs/features/bloggers/vacations).
* **Deadline extensions to review**: pending [extension requests](/docs/features/bloggers/deadlines).
* A subscription warning, shown to the store owner when the store [subscription](/docs/features/subscriptions) is about to expire.
The three blogger rows appear only when your permissions include **Manage Bloggers**.
## Permissions [#permissions]
On a store you own, the overview always loads. On a managed store, you need the **View Sales** permission to see it. The **New customers** tile follows your access to the Customers list, which **View Sales** includes. See [Store Managers](/docs/features/store-managers) for granting permissions.
## Related pages [#related-pages]
# Imports & Exports (/docs/features/imports-and-exports)
Allomancy Stores includes two Settings tools for moving sales data in and out of your store: a transaction import for bringing historical sales into Allomancy, and a transactions export for downloading your store's sales as a CSV or XLSX file.
This page covers both Settings tools. For bringing customer records (not sales) into Allomancy, see [Customer Import](/docs/features/customer-import).
## Where to find Imports & Exports [#where-to-find-imports--exports]
Both tools live on the Settings page at [stores.allomancy.com/settings](https://stores.allomancy.com/settings):
* **Import sales** opens the **Import Transactions** flow at [stores.allomancy.com/settings/import](https://stores.allomancy.com/settings/import).
* **Export sales** opens the **Export Transactions** flow at [stores.allomancy.com/settings/export](https://stores.allomancy.com/settings/export).
## Importing historical transactions [#importing-historical-transactions]
Use this flow to bring sales records from another system into Allomancy. The import accepts a CSV with one row per transaction and asks you to map each unique product name in that CSV to one of your existing dispenser items.
### Drop or pick a CSV file [#drop-or-pick-a-csv-file]
Drag a CSV onto the **1. Upload the CSV File** dropzone or click to select one. Allomancy reads the file.
### Associate products with dispenser items [#associate-products-with-dispenser-items]
The page shows a **Dispenser items mapping** table under **2. Product Associations**. Each unique product name from the CSV is listed in one row, and you pick an item from the dispenser item picker on the right.
Products without an association are highlighted in red and will be skipped during import. You can proceed with skipped rows after confirming, or go back and fix the associations first. If no products are associated at all, you must associate at least one before the import can proceed.
### Submit [#submit]
To start the upload, click Import Transactions
### Wait for processing [#wait-for-processing]
Your transactions are processed in the background, and Allomancy notifies you when the processing finishes. To start over with a different CSV, click Import New File
### Recognized CSV columns [#recognized-csv-columns]
Allomancy matches each CSV column name against familiar transaction words, regardless of capitalization. Most matches work when the word appears anywhere in the column name.
| If a column header contains | The value is read as |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `date`, `time`, or `created` | the transaction date |
| `payment`, `gross`, `price`, `cost`, or `paid` | the gross L$ amount the customer paid |
| `net`, `received`, `net amount`, or `earning` | the net L$ that ended up with the store |
| `amount` without separate gross or net columns | both gross and net at once |
| `location` or `region` | where the sale happened |
| `product` or `item` | the product name, matched against your dispenser items |
| `payer`, `from`, or `buyer` | the buyer's username |
| `payerid`, `payeruuid`, `fromkey`, `payer key`, or `buyer_key` | the buyer's UUID |
| `receiver`, `to`, `recipient`, or `owner_name` | the receiver's username |
| `receiverid`, `receiveruuid`, `tokey`, `receiver key`, `owner_key`, or `customer uuid` | the receiver's UUID |
Headers Allomancy does not recognize are skipped. A column named `order item id` marks the file as a Second Life Marketplace import; if no location column is present, imported rows use `MP Import` as the location.
## Exporting your transactions [#exporting-your-transactions]
The export flow lets you request a file of your store's transactions for an optional date range, then download it once Allomancy finishes preparing it.
### Pick an optional date range [#pick-an-optional-date-range]
The Export Transactions page has a date range picker next to the export button. By default, all your transactions are exported. Use the picker to limit the export to a specific window.
### Choose the format and options [#choose-the-format-and-options]
Pick **CSV** or **XLSX** from the format dropdown; CSV is the default. Optionally tick **Exclude redeliveries** to leave redelivery rows out, or **Include marketplace sales** to add your Second Life Marketplace sales.
### Request the export [#request-the-export]
Click Request export to request the file. Only one export prepares at a time, and a short cooldown follows each request before you can ask for another.
### Download when ready [#download-when-ready]
Past requests appear in the **Available Exports** table below the button. Each row shows the Start Date, End Date, Format, Ready status, Download button, and Generated date.
The Ready column shows while Allomancy is preparing the file, when the file is ready, and a red **Failed** when the export could not be generated. To download the file, click
To open an XLSX export in Microsoft's Office online viewer, click next to its download button; XLSX rows are the only ones that show it.
## Permissions [#permissions]
Settings, including the Imports & Exports tools, is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Features (/docs/features)
Allomancy makes the common store jobs easy to manage from the website, while your inworld objects keep handling payments and delivery. Explore the guides below to get familiar with the web tools for managing your store.
# Marketplace Listings (/docs/features/marketplace-listings)
Marketplace Listings is where you review which Second Life Marketplace listing maps to which Allomancy product. Once a listing is associated with a product, Marketplace activity for that listing can be reviewed alongside the rest of your sales data.
## Where to find Marketplace Listings [#where-to-find-marketplace-listings]
Open the **Products** page at [stores.allomancy.com/products](https://stores.allomancy.com/products) and click Marketplace Listings in the top-right toolbar. The page lives at [stores.allomancy.com/products/marketplace-listings](https://stores.allomancy.com/products/marketplace-listings).
## Reading the listings table [#reading-the-listings-table]
The page helps you associate Marketplace listings with Allomancy products. An **Exclude demos** toggle at the top hides listings whose name contains "demo".
The table has three columns:
* **MP Item**: the Marketplace listing's item name. Rows that are not yet linked to an Allomancy product appear in muted text so you can spot them quickly.
* **Product**: a product picker. When the listing is already linked, the picker shows the current product. When it is not, the picker is empty.
* **Date Added**: when Allomancy first saw the listing.
The page lists the Marketplace listings Allomancy already knows about for your store. When a Marketplace sale comes in for a listing that is not associated yet, Allomancy tries to match it to a product by name.
Automatic name matching can occasionally choose the wrong product if multiple products have the same or very similar names. Review this page after Marketplace sales to confirm each listing is associated with the correct Allomancy product.
## Associating a Marketplace listing with a product [#associating-a-marketplace-listing-with-a-product]
### Find the listing [#find-the-listing]
Use the search box to look up a Marketplace item by name, and turn on **Exclude demos** if you want to hide demo entries.
### Open the Product picker [#open-the-product-picker]
Click the **Product** cell on the row. The picker shows your Allomancy products.
### Pick the matching product [#pick-the-matching-product]
Select the product that should be linked to this Marketplace listing. The change takes effect right away.
To replace an existing association, open the picker on the same row and choose a different product. The previous association is overwritten.
This is also how you correct an automatic name match that picked the wrong product.
## How Marketplace activity shows up in Sales [#how-marketplace-activity-shows-up-in-sales]
Marketplace transactions appear on the [Sales](/docs/features/sales) tabs alongside system sales. The **Source** filter at the top of the Sales pages lets you switch between **System**, **Marketplace**, and **Both**. On the Locations chart, Marketplace transactions are folded into a single `* Marketplace *` row so you can compare your inworld locations to your Marketplace activity at a glance.
## Permissions [#permissions]
The Marketplace Listings page is reached from the Products page, so opening it needs the same access as **Products**. Editing an association requires the **Manage Products** permission. Store owners always have it. To grant it to a staff member, see [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Mass Edit (/docs/features/mass-edit)
Mass Edit updates matching products from the [Products page](https://stores.allomancy.com/products). Click to open a four-step dialog: **Filters**, **Basic Info**, **Extra Config**, and **Profit Share**.
## Filtering products [#filtering-products]
Use filters to choose which products Mass Edit touches.
* **Product name filter**: matches products whose names contain the text you enter.
* **Minimum Price** and **Maximum Price**: limit the update to products inside a L$ price range. When you leave the range at its defaults, the price filter includes every product.
* **Category filter**: turn this on to pick a category. When it is off, category is not used as a filter.
## Choosing what to update [#choosing-what-to-update]
The **Price (L$)** and **Image UUID** fields are always visible on **Basic Info**. Leave either field blank when you do not want Mass Edit to change that value.
For most other product details, choose exactly which changes to apply. Turn on the matching update control before setting the value you want to use.
### Basic Info [#basic-info]
* **Update demo**: unlocks the **Demo item** picker, filtered to demo items inworld.
### Extra Config [#extra-config]
* **Update permissions**: unlocks **Copy**, **Modify**, and **Transfer** checkboxes.
* **Update Bloggable**: unlocks the **Bloggable** switch.
* **Update credit block**: unlocks the **Block Credits** switch.
* **Update limited**: unlocks the **Limited** switch. When **Limited** is on, also set **Available amount**.
### Profit Share [#profit-share]
**Update Profit Share** unlocks the profit-share editor. For more details, see [Profit Share](/docs/features/profit-share).
Enabling **Update Profit Share** with no rows clears the existing product profit share for every matching product. To leave profit shares unchanged, keep **Update Profit Share** off.
## Confirming the update [#confirming-the-update]
A Mass Edit run with no **Product name filter** and no **Category filter** touches every product within the selected price range. Read the confirmation before continuing.
Use Update to review the confirmation.
When at least one product-name or category filter is set, a dialog titled **Apply the changes?** opens.
When no product-name filter and no category filter are set, the **Warning: No filters!** dialog warns that every product within the selected price range will be affected.
## Permissions [#permissions]
Mass Edit is part of the Products page and needs the **Manage Products** permission. Store owners always have it. To grant it to a staff member, see [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Mass Product Listing (/docs/features/mass-listing)
Mass listing saves you from creating the same product form over and over. Use it when your dispenser already contains a batch of unlisted items, such as color variants or matching pieces that should share the same starting price.
## How it works [#how-it-works]
On the [products page](https://stores.allomancy.com/products), click to open a dialog that asks for:
* **Price**, applied to every item in the operation
* **Image UUID**, optional, applied to every item
* **Filter**, optional, used to match item names
If you leave the filter blank, every unlisted item in your dispenser is processed. If you provide a filter, only unlisted items whose names include that term are listed.
## Example [#example]
Say your dispenser holds these unlisted items:
* Fancy shoes
* Fancy hat
* Fancy pants
* Gorgeous shirt
Setting the price to L$100 and the filter to `fancy` lists the three items with `fancy` in their names. Gorgeous shirt stays unlisted because its name does not match the filter.
## Related product tools [#related-product-tools]
# News Kiosk (/docs/features/news-kiosk)
The News Kiosk gives you a web-based way to reach customers when you have store news to share. It is useful for updates that should be easier to send and easier to keep available than a normal inworld group notice.
## Why it matters [#why-it-matters]
Group notices require you to be online in Second Life, and they can disappear from customer inboxes quickly. With the News Kiosk, you can write the message from Allomancy Stores and let customers read it on the web when they need it.
## Where to find News Kiosk [#where-to-find-news-kiosk]
The News Kiosk lives at [stores.allomancy.com/kiosk](https://stores.allomancy.com/kiosk). Two header buttons take you to the related areas:
* View Posts opens the Kiosk Posts page at [stores.allomancy.com/kiosk/posts](https://stores.allomancy.com/kiosk/posts), which lists every post you have published.
* Manage Subscribers opens the News Subscribers page at [stores.allomancy.com/kiosk/subscribers](https://stores.allomancy.com/kiosk/subscribers).
## Publishing a post [#publishing-a-post]
The main News Kiosk page is a compose form titled **New message**.
### Write your message [#write-your-message]
Type your message into the **New message** area.
You can insert two personalization placeholders that are filled in for each recipient:
* Name inserts `{name}`, replaced with the recipient's first name.
* Username inserts `{username}`, replaced with the recipient's username.
An emoji picker next to the placeholder buttons lets you drop emojis into the text.
### Optionally attach an item [#optionally-attach-an-item]
Use the **Item to deliver** picker below the message to choose an item from one of your rezzed dispensers. The item is sent along with the message.
### Optionally mark the item as On demand [#optionally-mark-the-item-as-on-demand]
Turn on **On demand** if customers should be able to retrieve the attached item again later from the website, including customers who subscribe after this post is published. Leave it off to deliver the item only at publish time.
### Publish [#publish]
To publish the post, click Publish
If your message contains placeholders, a **Preview** card below the form shows the message with your own profile substituted in, so you can see how it will look to a recipient.
## Managing published posts [#managing-published-posts]
The Kiosk Posts page lists every post you have published. Each row shows:
* **Message**: the post text, truncated to fit the row.
* **Item Attached**: the dispenser item attached to the post. You can change it from the row by picking a different item.
* **On Demand**: a toggle that flips the on-demand setting on or off for that post.
* **Date**: when the post was published.
* **Action**: a button that deletes the post.
The message text is not editable after publishing. To say something different, publish a new post and, if you want, delete the old one.
## How customers receive your posts [#how-customers-receive-your-posts]
Subscribers get your messages in two places:
* **Inworld**, when they are online. The local-chat copy of the message includes an unsubscribe link.
* **On the Allomancy customer website**, where they can read past posts in a feed that combines every store they are subscribed to. A store selector lets them narrow the feed to one store at a time.
When you attach an item to a post and turn on **On demand**, the customer website shows a **Get item** button on that post. Subscribers can click it to receive the item inworld at any time. Customers who are not currently subscribed to your store can read your posts but cannot use that button.
## How customers subscribe and unsubscribe [#how-customers-subscribe-and-unsubscribe]
Customers manage their own subscription through the **News Kiosk terminal** you rez inworld. The terminal lets them subscribe to your store or unsubscribe later.
Every kiosk message you publish also includes an unsubscribe link in its local-chat copy, so customers can unsubscribe with a single click and do not need to find the inworld terminal again just to opt out.
## Managing subscribers [#managing-subscribers]
The News Subscribers page is the merchant-side list of everyone currently subscribed to your kiosk. Customers can also manage their own subscription themselves, as described in [How customers subscribe and unsubscribe](#how-customers-subscribe-and-unsubscribe) above.
### Adding a subscriber [#adding-a-subscriber]
Click the button at the top of the page, type the resident's Second Life **Username**, and click Add to add the subscriber.
### Removing a subscriber [#removing-a-subscriber]
To remove the resident from the subscribers list, click the button on their row.
### Importing subscribers from CSV [#importing-subscribers-from-csv]
The button next to the add button opens the News Subscriber import flow. The import accepts a CSV with at least one column whose header contains **name** or **customer** (the username). A column whose header contains **key** or **uuid** is also picked up. Any other column is ignored in subscriber import mode.
The customer's profile on the [Customers page](/docs/features/customers) shows a **News Subscriber** column that reflects whether they currently receive your kiosk posts. Adding or removing a subscriber here updates that column.
## Permissions [#permissions]
The News Kiosk is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Payment Troubleshooting (/docs/features/payments-troubleshooting)
When a sent payment does not go through, you can usually find the cause from two places: a status icon and an optional message. This page walks through what each status looks like, what to check first, and what the underlying error codes mean if they show up in the message text or in a support conversation.
The Sent Payments tab lives at [stores.allomancy.com/payments/sent](https://stores.allomancy.com/payments/sent).
## Where you see failed payments [#where-you-see-failed-payments]
Failed payments show up on the **Sent Payments** tab. Each row has:
* A **Status** icon.
* A **Message** column with extra detail.
* A **Source** column showing what kind of payment the row is.
You can narrow the list to a single category from the payment-type dropdown above the table: **All**, **Manual**, **ProfitShares**, **Fees**, **Refunds**, **Subscriptions**, or **Other**.
## Reading the status icon [#reading-the-status-icon]
| Status | What it means |
| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| **Requested** | Allomancy has queued the payment but has not started sending it yet. |
| **Processing** | The payment is being sent to a vendor for delivery. |
| **Success** | The payment completed. A short Transaction ID appears in the row. |
| **Failed** | The payment did not go through. |
| **Error** | An unexpected problem stopped the payment. |
Failed and Error rows both use the same red alert icon, so check the **Status** and **Message** columns for details.
## What to check first [#what-to-check-first]
Most failed payments come down to one of these issues.
* **At least one of your vendors is rezzed inworld.** Allomancy sends L$ through your rezzed vendors. With nothing rezzed, no send can succeed.
* **The vendor has debit permission.** When you rez a vendor, Second Life asks whether to grant debit permission. Without it, the vendor cannot spend L$ on your behalf.
* **The recipient username is spelled correctly.** The Send L$ dialog looks the username up before the confirmation step, but a misspelled name in another flow can still fail.
* **Your sending balance covers the amount.** The L$ comes out of the avatar that owns the rezzed vendor, so that avatar needs enough L$ available.
* **You have not sent too many payments too quickly.** If you have just sent many payments in quick succession, wait several minutes before trying again.
Allomancy does not retry a failed payment. While a payment status is **Processing**, Allomancy automatically retries for several minutes (covering cases where no vendor was rezzed inworld at the time, or where the funds were insufficient). Once the status becomes **Failed** or **Error**, no further retries occur. To send the same payment again, open Send L$ on the [Payments page](/docs/features/payments) and submit a new request.
## Error codes [#error-codes]
Allomancy and Second Life's L$ system use short codes for specific failure reasons. If support mentions one of these codes, refer to the table below to see what it means.
| Code | What it likely means | What to check |
| --------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `NO_VENDORS_FOUND` | No rezzed vendor was available to send the payment. | Rez at least one vendor inworld and try again. |
| `LINDENDOLLAR_INSUFFICIENTFUNDS` | The sender's L$ balance was too low. | Top up the L$ balance of the avatar that owns your vendors. |
| `LINDENDOLLAR_ENTITYDOESNOTEXIST` | Second Life could not find the recipient avatar. | Double-check the spelling of the recipient's username. |
| `LINDENDOLLAR_BADCLOCKSKEW` | Second Life rejected the transfer for a timing reason. | Wait a moment and try again. If it persists, contact support. |
| `INVALID_DESTINATION` | The recipient could not be used as the destination for this payment. | Check the recipient username and try again. If the username is correct, contact support. |
| `INVALID_AMOUNT` | The amount was outside the allowed range. | Use a whole number between L$1 and L$9,999,999. |
| `THROTTLED` | The Second Life payment system temporarily rate-limited the request. | Wait several minutes before trying again. |
| `MISSING_PERMISSION_DEBIT` | The vendor does not have permission to spend L$. | Re-rez the vendor and grant debit permission when Second Life asks. |
| `GROUP_OWNED` | The object that was supposed to send the payment is owned by a group instead of by you. | Take the vendor back into inventory and re-rez it under your own avatar. |
| `TRANSFERS_DISABLED` | The transfer could not be completed because transfers are disabled or blocked. | Confirm that the sender and recipient can normally send and receive L$, then contact support if the issue continues. |
| `EXPIRED` | The payment request was not processed in time. | Send the payment again. |
| `SERVICE_ERROR` | A general error occurred while processing the payment. | Wait a few minutes and try again, or contact support if the issue continues. |
## When to contact support [#when-to-contact-support]
If a payment keeps failing after you have worked through the checks above, or if you see `SERVICE_ERROR` more than once, email [archis@allomancy.com](mailto:archis@allomancy.com) with:
* The receiver username
* The amount
* The approximate time of the failed payment
* The status (Failed or Error) and any text from the **Message** column
Including the status and the message text helps the team trace the specific failed payment in the system.
## Related pages [#related-pages]
# Payments (/docs/features/payments)
The payments page gives you a web view of money moving through your store. You can review sales-related activity, profit shares, subscription charges, commission fees, and manual L$ payments without logging inworld just to check history.
The page is organized into two tabs, **Sent Payments** at [stores.allomancy.com/payments/sent](https://stores.allomancy.com/payments/sent) and **Received Payments** at [stores.allomancy.com/payments/received](https://stores.allomancy.com/payments/received). A date range picker and a payment-type filter sit above each tab.
Payments sent from your store use your rezzed vendors. You need at least one active vendor inworld for outgoing payments, including manual sends, to succeed.
## Sending a L$ payment [#sending-a-l-payment]
The Sent Payments tab shows a button at the top of the page that opens the Send L$ dialog. On a brand-new account, you may see Enable L$ payments instead. This enables manual L$ sends from the web and requires an inworld confirmation step.
### Enabling web L$ payments [#enabling-web-l-payments]
### Click Enable L$ payments [#click-enable-l-payments]
On the Sent Payments tab, click Enable L$ payments to start the flow. Allomancy sends a link to your avatar in Second Life so you can authorize the feature.
### Open the link inworld [#open-the-link-inworld]
Open the link in Second Life to finish enabling the feature.
### Sending a payment [#sending-a-payment]
On the Sent Payments tab, click Send L$, enter the **Receiver Username** and **Amount (L$)**, and optionally add a **Message**. Allomancy looks up the recipient so you can review their avatar picture and the amount before sending. One of your rezzed vendors handles the actual transfer when you click Pay
If a payment fails after confirmation, see [Payment Troubleshooting](/docs/features/payments-troubleshooting) for what each status means and what to check.
You can disable web L$ payments from inside the Send L$ dialog using the Disable payments button. Re-enabling later goes through the inworld confirmation again.
## Reviewing sent payments [#reviewing-sent-payments]
The Sent Payments tab is your record of every L$ that has left your store: sales-related fees, profit shares, subscription charges, refunds, and any manual sends you have made.
The chart at the top groups payments by recipient, ranked by total L$. Below the chart, a single-line **Total** sums the visible payments, with a "(Refunds excluded)" note when refunds are filtered out.
Each row in the table shows:
* **Receiver**: the resident who received the payment.
* **Amount**: the L$ value.
* **Status**: see [Payment statuses](#payment-statuses) below.
* **Message**: any text returned by the payment system.
* **Source**: where the payment came from, such as **Web Request**, **Subscription**, or **Profit Share**. See [Where each payment came from](#where-each-payment-came-from).
* **Transaction ID**: a short identifier shown only for completed payments, with a copy button. Payments received through a Payment Terminal never carry one: scripts cannot capture the transaction ID for terminal payments.
* **Date**: the payment date.
Use the search box to find rows by name, the date range picker to pick a window (the default is **All time**), and the payment-type dropdown to filter the list to **All**, **Manual**, **ProfitShares**, **Fees**, **Refunds**, **Subscriptions**, **Terminal**, or **Other**.
If a row shows the red alert icon, head to [Payment Troubleshooting](/docs/features/payments-troubleshooting) for a checklist of likely causes and a reference table of the underlying error codes.
## Reviewing received payments [#reviewing-received-payments]
The Received Payments tab shows the same kind of information from the other side: payments that came into your store. Use it to review profit-share income from products you collaborated on, refunds processed back to you, or any manual L$ another resident has sent you through Allomancy.
The tab opens with a chart of payments grouped by payer, a Total summary, and a table with these columns:
* **Payer**: the resident who paid.
* **Amount**, **Status**, **Message**, **Source**, **Transaction ID**, and **Date**: same meanings as on the Sent Payments tab.
The payment-type filter on this tab is narrower: **All**, **Manual**, **ProfitShares**, **Refunds**, **Terminal**, or **Other**. To send L$ manually, use the Send L$ flow on the Sent Payments tab.
## Payment statuses [#payment-statuses]
Failed and Error rows both use the same red alert icon. For the full status reference and practical guidance on what to check when a payment does not go through, see [Payment Troubleshooting](/docs/features/payments-troubleshooting).
## Where each payment came from [#where-each-payment-came-from]
Every row in the Sent and Received tables has a **Source** column. Source labels you may see include:
* **Basic Plan Fee**: a basic plan or commission fee.
* **Subscription**: an Allomancy subscription charge.
* **Web Request**: a payment you initiated from the website.
* **Profit Share**: a profit share related to a product.
* **Refund**: a refunded transaction.
* **Scheduled**: a scheduled payment.
* **Coupon Share**: a payout related to coupons.
* **Referral**: a payout related to referrals.
* **Payment Terminal**: a custom inworld payment recorded by a [Payment Terminal vendor](/docs/features/vendors).
The payment-type filter dropdown above each table groups these sources into a smaller set of practical buckets (**Manual**, **ProfitShares**, **Fees**, **Subscriptions**, **Refunds**, **Terminal**, **Other**) so you can quickly narrow the list when you only want to see one kind of payment.
Your payment history stays available when you are offline in Second Life, so you can reconcile earnings on your own schedule.
## Related pages [#related-pages]
# Product Listing (/docs/features/product-listing)
The single-product listing flow lives on the [Products page](https://stores.allomancy.com/products). Click to open the **Product Setup** dialog and create one listing at a time. For the first-time walkthrough, see [Product Listing](/docs/quickstart/product-listing).
## Setting basic product info [#setting-basic-product-info]
The **Basic Info** step connects the listing to the item that will be delivered and sets its basic sale details.
* **Item to be delivered**: an item picker for dispenser contents. Three filter modes control what appears:
* **Unlisted Only**: items not yet associated with any product and whose name does not contain "demo".
* **All Items**: every dispenser item, listed and unlisted, whose name does not contain "demo".
* **Demos Only**: items whose name contains "demo".
* **Price (L$)**: the product's price in Linden dollars.
* **Image UUID**: the Second Life texture UUID for the product image.
* **Demo item**: an optional second item picker for a demo version of the product. This adds a "Demo" button to the menu of the vendor that product is associated with.
If an item you placed in the dispenser does not appear in any picker mode, you may not have transfer permission over it. A single no-transfer object inside the item (such as a script) can make the whole item non-transferable for you, and the dispenser ignores items it cannot transfer. Before troubleshooting permissions, also confirm you are looking in the right mode: **All Items** if the item is already associated with a product, or **Demos Only** if its name contains "demo".
## Configuring extra product options [#configuring-extra-product-options]
The **Extra Config** step controls optional product behavior and display details.
* **Copy**, **Modify**, and **Transfer**: tell Allomancy what permissions the inworld item carries. The **Copy** flag is the most important one: customers cannot request a redelivery of a product marked as no-copy, so make sure it matches the actual permissions of the item in your dispenser.
* **Bloggable**: makes the product available to be retrieved by your bloggers on the blogger-facing site at [bloggers.allomancy.com](https://bloggers.allomancy.com). No effect on what customers see or pay.
* **Block Credits**: prevents customers from paying for this product with store credit. If a customer attempts to use their credit (or pays a partial L$ amount expecting credit to cover the rest), Allomancy refunds the payment and sends a local-chat message explaining that store credit cannot be used for this product.
* **Limited**: enables a stock-count field for this product. When **Limited** is on, you set the number of copies available. Each purchase decreases the count by one; when the count reaches zero, the product becomes **Sold Out** and cannot be purchased. Restock by raising the count. Useful for limited-edition releases.
* **Product name**: the customer-visible name for this product. It auto-fills from the item picked in **Item to be delivered**, and must be unique within your store.
* **Product Description (For bloggers)**: an optional description shown to bloggers.
The **Copy**, **Modify**, and **Transfer** checkboxes describe how Allomancy treats the product. They do not change the actual Second Life object permissions inside the dispenser.
## Splitting profit on the product [#splitting-profit-on-the-product]
The **Profit Share** step lets you split a product's profit among multiple residents.
Type a username and click Add to look it up. Each added row has a percent field and to remove the row. A running total below the list shows the combined percentage, which cannot exceed 100%.
For the profit-share limits and how product profit shares differ from vendor profit shares, see [Profit Share](/docs/features/profit-share).
For how product discounts compose with vendor, customer, loyalty, and coupon discounts at purchase time, see the [Loyalty discounts](/docs/features/customers#loyalty-discounts) section on the Customers page.
## Saving [#saving]
When finished, click Save
## Permissions [#permissions]
The Products page is visible to staff with product access. Creating and editing products is part of the **Manage Products** permission described in [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Product Tools (/docs/features/product-tools)
Beyond listing products, Allomancy gives you a few practical tools for everyday product care. Use these when you need to run a sale, send an item to a resident, redeliver a product to past customers, or group related products together.
## Setting product discounts [#setting-product-discounts]
On the [products page](https://stores.allomancy.com/products), click to open a window with three discount modes and a value field.
### Percent [#percent]
Use **Percent** for a percentage discount. For example, if a product costs L$100 and you enter `20`, the product price becomes L$80.
### Flat [#flat]
Use **Flat** to subtract a fixed L$ amount from the current price. For example, if a product costs L$800 and you enter `10`, the product price becomes L$790.
### Override [#override]
Use **Override** to replace the current price with the value you enter. For example, if a product costs L$600 and you enter `40`, the product price becomes L$40.
## Sending a product [#sending-a-product]
On the [products page](https://stores.allomancy.com/products), click on the product, enter one or more Second Life usernames separated by commas, and optionally add a message that will appear in the recipient's local chat.
## Running a bulk redelivery [#running-a-bulk-redelivery]
Bulk redelivery sends a product again to every customer who owns that product.
From the [products page](https://stores.allomancy.com/products), click on the product, choose the **Mass redelivery** tab, optionally add a message, and click **Mass Redeliver**.
## Managing categories [#managing-categories]
Categories help you group related products. On the [categories page](https://stores.allomancy.com/categories), type a category name and use Add Category to create it. Use on a category row to open that category.
On the category page, drag products from **Other products** into **In Category**. Use **Move** to add every currently displayed product from **Other products**, or **Remove** to take every currently displayed product out of **In Category**.
Once a category has products, use to apply a discount to the whole group. You can also choose a **Webhook** for the category so sales from those products post to a specific channel or server; see [Discord Notifications](/docs/features/discord-notifications) for webhook setup.
# Profit Share (/docs/features/profit-share)
Profit share is how you let collaborators take a slice of each sale. Use it for co-creators on a product, region partners who host a vendor, blogger arrangements, or any other split where the L$ should not all go to one avatar.
## Where profit shares live [#where-profit-shares-live]
Profit shares are configured in two places, and they are stored separately:
* **Per product**, on **Step 3: Profit Share** of the product edit dialog. This split is part of the product and applies wherever the product is sold.
* **Per vendor**, on **Step 4: Profit Shares** of the vendor edit dialog. This split applies only to sales from that specific vendor.
Both editors use the same recipient-and-percentage form.
Vendors that have at least one profit-share recipient show a yellow icon in the Vendors list **State** column, so you can spot them at a glance.
## Adding a recipient [#adding-a-recipient]
The form looks the same on both the product and vendor side.
Type the recipient's Second Life **username** at the top of the Profit Share step, click Add to look them up, then set the **percentage** in the new row (defaults to 1). Save the product or vendor dialog as you normally would; the list of profit shares is saved together with the rest. If the username is not found or is already on the list, Allomancy prompts you to try again or choose someone else.
## Editing or removing a recipient [#editing-or-removing-a-recipient]
To change a percentage, click the percent field on a row and type a new value.
To remove a recipient, click on the right of that row.
## Limits [#limits]
The form enforces three rules:
* Each row's percentage must be between **1** and **100**.
* The **total** of all percentages must be **at most 100**.
* The same resident cannot appear twice in the same list.
The footer below the rows shows a running total. When the total exceeds 100, the footer turns red with the message "Total percentage exceeds 100%" and the form refuses to save.
## How payouts appear in Payments [#how-payouts-appear-in-payments]
Profit-share activity appears on the [Payments](/docs/features/payments) tabs:
* On the **Sent Payments** tab with **ProfitShare** in the Source column when your store pays a collaborator.
* On the **Received Payments** tab with **ProfitShare** in the Source column when you are on the receiving end of someone else's profit share.
Both tabs offer a **ProfitShares** filter so you can narrow the list to profit-share movements only.
## Permissions [#permissions]
Editing **product** profit shares needs the **Manage Profit Share** permission. Store owners always have it. Editing **vendor** profit shares is part of the **Manage Vendors** permission, because vendor profit shares are configured inside the vendor edit dialog.
To grant either permission to a staff member, see [Store Managers](/docs/features/store-managers).
When both the product and the vendor selling it have profit shares set, the vendor profit shares apply first; the product profit shares are then calculated on what remains.
**Example.** A product is sold for L$100. The vendor has a 10% profit share to one collaborator, and the product has a 20% profit share to a different collaborator. The vendor profit share takes L$10 first (10% of L$100). The product profit share is then applied to the remaining L$90, sending L$18 to the second collaborator (20% of L$90). The store keeps L$72.
If the store does not have an active subscription, Allomancy's 5% transaction fee is deducted from the gross price first; profit shares are then calculated on the post-commission amount.
L$ amounts are integers; any calculation that produces a fractional L$ is rounded down.
## Related pages [#related-pages]
# Sales & Reporting (/docs/features/sales)
The Sales section gives you four ways to look at the same transactions: a table for individual sales and three charts that group sales by date, by product, or by location. Use it to investigate a single purchase, watch your store's revenue trend, see your best-selling products, or compare regions.
The Sales pages live at [stores.allomancy.com/sales](https://stores.allomancy.com/sales).
## Switching between sales views [#switching-between-sales-views]
Across the top of the page you can switch between:
* **Table**: every transaction in a paginated list.
* **Dates**: revenue or quantity over time, grouped by day, month, or year.
* **Products**: revenue or quantity by product.
* **Locations**: revenue or quantity by region or sale source.
Visiting `/sales` directly takes you to the Table tab.
## Filtering sales [#filtering-sales]
A few filters appear above every tab, but not all of them apply to every view:
* **Date range**: pick a custom range or a preset. Defaults to the last 30 days. *Disabled on the Table tab.*
* **Location**: limit results to a single store location. *Disabled on the Table tab.*
* **Source**: choose **System** (inworld vendor sales), **Marketplace** (SL Marketplace), or **Both**. Hidden on the Locations chart, which already groups by source.
* **Group by** (Dates chart only): **Daily**, **Monthly**, or **Yearly**.
* **View** (charts only): show **Gross**, **Net**, **Quantity**, or **Gross & Net** side by side.
Filter selections persist for the rest of your browser session.
## Reading the totals [#reading-the-totals]
The summary card on each chart shows three numbers, which match the chart series:
* **Gross**: the total L$ customers paid before any deductions.
* **Net**: the L$ kept by your store after deductions.
* **Amount**: the number of transactions in the range.
## Reviewing individual sales [#reviewing-individual-sales]
The Table tab lists individual transactions. Each row shows:
* **Product**: the product name. Struck through in gray if the product has since been deleted, struck through in red if the transaction itself was deleted.
* **Receiver**: the customer who received the product. Hovering the name opens a quick profile popover.
* **Info**: status icons that surface gift purchases, redeliveries, gacha pulls, and any credits or customer discount used. Hover an icon for the details.
* **Paid**: gross L$ the customer paid.
* **Net**: net L$ for the transaction after deductions.
* **Location**: where the sale happened. Click an inworld region name to open the SLurl in a new tab.
* **Date**: date and time of the sale.
* **Action**: redeliver this product to the receiver.
* **Status**: green check for delivered, red X for failed delivery, yellow refresh while the product is still being delivered.
### Searching and filtering the table [#searching-and-filtering-the-table]
Two icon toggles above the table keep the results tidy:
* **Hide L$0 transactions** (on by default). Hides gifts, comps, and other zero-value rows.
* **Hide deleted products** (on by default). Hides rows where the product has been deleted from your store.
The **Source** filter from the top of the page also applies to the table, so you can show only System sales, only Marketplace sales, or both.
### Deleting transactions [#deleting-transactions]
Tick the checkbox on one or more rows to enable the bulk delete button, then review the listed rows before confirming.
Deleting a transaction does not remove the customer's right to request a redelivery for that product. To stop redeliveries, remove the purchase from the customer's profile on the [Customers page](/docs/features/customers).
## Reading the Dates chart [#reading-the-dates-chart]
The Dates chart plots **Gross**, **Net**, **Quantity**, or **Gross & Net** over time. Use the **Group by** filter to switch between Daily, Monthly, and Yearly buckets. The summary card above the chart sums up the visible range.
The Source filter applies here too: switch to System or Marketplace to see only those sales.
## Reading the Products chart [#reading-the-products-chart]
The Products chart ranks your products by gross, net, or quantity for the selected period. Each bar is labeled with that product's share of the total. Gacha purchases are included alongside regular system sales when they appear in the selected source.
### Grouping with tags [#grouping-with-tags]
The tag input above the chart lets you collapse many products into a few buckets by partial name match. Type a tag like `black` or `shirt`, and any product whose name contains that string is folded into a `black` or `shirt` bar. Anything that matches no tag goes into a `* Others *` bar. This is useful for comparing color variants, product lines, or item types at a glance.
## Reading the Locations chart [#reading-the-locations-chart]
The Locations chart ranks the regions where your sales happened. SL Marketplace and Marketplace imports are folded into a single `* Marketplace *` row, so you can compare your inworld locations against your Marketplace activity side by side.
## Related pages [#related-pages]
# Store Credit (/docs/features/store-credit)
Store credit is a L$ balance a customer can spend at your vendors instead of paying out of pocket. Each customer carries two balances: **Store credit**, which works for any purchase, and **Restricted credit**, which the customer can spend on themselves but cannot use for gifting. Any credit you grant can be given an expiry date, so a promotion or a goodwill gesture does not sit unused forever.
## Opening a customer's credit [#opening-a-customers-credit]
Credit is managed per customer from the [Customers page](https://stores.allomancy.com/customers). Click on a customer's row to open the customer drawer, then switch to the **Credits** tab. The [Customers](/docs/features/customers) page guide covers the rest of the drawer.
The drawer header always shows the customer's **Store credit** and **Restricted credit** totals. These are the balances the customer can spend right now: expired credit is already excluded.
## How credit expiry works [#how-credit-expiry-works]
On the **Credits** tab, each balance is broken down into two parts:
* The **Never-expiring balance** holds credit granted without an expiry. It never lapses.
* **Expiring lots** are individual grants, each with its own expiry date. Once a lot passes its date, that credit no longer counts toward the customer's balance.
When the customer spends credit, expiring lots are used before the never-expiring balance, and the lot with the soonest expiry is used first. This spends credit that expires soon before longer-lived credit.
Expiry dates on the tab are shown in Second Life time; hover a date to see it in your local time. A lot expiring within 30 days also carries a countdown badge, which turns red once the expiry is 7 days away or less; hover it for the exact countdown.
When a customer redeems a [gift card](/docs/features/gift-cards), the credit arrives as its own lot marked with a badge, both in the lot list and later in the credit history.
## Adding credit [#adding-credit]
To grant credit, click Add credit in the **Store credit** or **Restricted credit** section, enter the amount, pick an expiry, and click Add credit
The **Expiry** presets are **No expiry**, **30d**, **60d**, **90d**, **1 year**, and **Custom…**, which opens a calendar for an exact date. Leaving **No expiry** selected adds the amount to the never-expiring balance; any other choice creates a new expiring lot.
## Editing a lot [#editing-a-lot]
To change a lot's amount or expiry, click on its row, adjust either or both, and click Save lot
An existing lot always keeps an expiry date: the editor lets you move the date, not remove it.
## Revoking a lot [#revoking-a-lot]
To take back a grant before it expires, click on the lot's row.
Revoking is immediate and cannot be undone: the customer loses that credit right away. To reinstate it later, add a new grant.
## Setting the never-expiring balance [#setting-the-never-expiring-balance]
To set the never-expiring part of a balance to an exact amount, click Edit balance next to **Never-expiring balance**, enter the new amount, and click Save balance
Unlike adding credit, this sets the balance rather than adjusting it, and it only affects the never-expiring portion: the customer's expiring lots are left untouched.
## Reviewing the credit history [#reviewing-the-credit-history]
At the bottom of the **Credits** tab, click Credit history to review credit that is no longer active. Events are grouped into **Store credit** and **Restricted credit**, and each entry shows when the lot closed, the amount that was granted, and a badge explaining why it closed:
* **Expired**: the lot reached its expiry date.
* **Superseded**: the lot was replaced when the customer's credit was set to an exact amount.
* **Revoked**: a grant you took back with the revoke action.
## Expiry on bulk adjustments and new customers [#expiry-on-bulk-adjustments-and-new-customers]
Two other flows can attach an expiry to credit:
* **Bulk credit adjustments**: the Bulk Credits dialog on the Customers page has an **Expiry (applies to added credit)** field. The expiry applies only to credit you add; the dialog rejects an expiry when you are reducing credit.
* **New customers**: when you add a customer manually, you can seed their starting credit with an expiry using the same presets.
For both flows themselves, see [Customers](/docs/features/customers).
## Default expiry for earned credit [#default-expiry-for-earned-credit]
Automatically granted credit, such as purchase bonuses and coupon-share credit, can take a default expiry from **Bonus credit expiry (days)**, in the Bonus credit dialog you open from [Store Settings](/docs/features/store-settings). Leave it empty and that credit never expires.
## Permissions [#permissions]
The Customers page is visible to the store owner and to managers with the **View Sales** or **Manage Customers** permission. Editing customers, including their credit, is part of **Manage Customers**. See [Store Managers](/docs/features/store-managers) for granting permissions.
## Related pages [#related-pages]
# Store Managers (/docs/features/store-managers)
The Store Managers page lets the store owner give other Second Life residents access to your store. Each manager has a tailored set of permissions, so you can hand off blog approvals or vendor maintenance without giving them full access to your store.
The page lives at [stores.allomancy.com/store-managers](https://stores.allomancy.com/store-managers).
Only the store owner can add or change managers. Managers themselves cannot open this page from a store they manage.
## Reviewing your managers [#reviewing-your-managers]
Each row shows a manager's username and a status icon for each of their permissions: a green when the permission is granted, a red when it is not. The columns are **View Sales**, **Send Product**, **Manage Products**, **Manage Bloggers**, **Manage Customers**, **Manage Bots**, **Gift Cards**, and **Profit Shares**. The right-most **Action** column opens the edit dialog.
The list is sorted by username by default.
The **Manage Vendors** permission is configured in the edit dialog but does not have its own column on the list.
## Adding a manager [#adding-a-manager]
Click above the managers list, enter the resident's Second Life **Username**, tick only the permissions the manager needs, and use Add to add the manager. See the [permissions table below](#permissions) for what each permission unlocks.
## Editing or removing a manager [#editing-or-removing-a-manager]
Click on a manager's row to open the edit dialog. The permission checkboxes pre-fill with the manager's current state.
* Toggle the checkboxes you want to change and click Update to save.
* To revoke the manager entirely, click Remove
**Remove** takes effect immediately. There is no extra confirmation. The manager loses access to your store as soon as you click the button.
## Switching to a managed store [#switching-to-a-managed-store]
Once you have been added as a manager on another store, that store appears in the store selector at the top of the sidebar. Click the selector to see your owned store alongside any stores you can manage, then pick one to switch.
When you switch to a managed store, the sidebar updates to show only the pages your permissions allow. Pages that are reserved for the owner of that store are hidden.
## Leaving a store as a manager [#leaving-a-store-as-a-manager]
If you no longer want to manage another resident's store, you can step down yourself.
Use the store selector in the sidebar to make the managed store active. Then go to the home page and click next to the store name.
## Permissions [#permissions]
Each permission you tick in the edit dialog is a bundle of capabilities. The table below describes what a manager gets when you grant each one on a store you own.
| Permission | What the manager can do |
| ----------------------- | ---------------------------------------------------------------------------------- |
| **View Sales** | Open the Sales pages and view the Customers list. |
| **Send Products** | Open the Products page and use the Send and Mass Redeliver actions. |
| **Manage Products** | Open and manage the Products page, including create, edit, delete, and Categories. |
| **Manage Bloggers** | Full Blog Dashboard, including applications, posts, deadlines, and bloggers. |
| **Manage Customers** | View and edit customers, including credit and per-customer discount fields. |
| **Manage Bots** | Open the Bots page. |
| **Manage Profit Share** | Edit the Profit Share tab on a product listing. |
| **Manage Vendors** | Open the Vendors page and manage API keys. |
| **Manage Gift Cards** | Open the Gift Cards page. |
When working inside a managed store, managers do not see Payments, Gachas, News Kiosk, Subscriptions, Tools, Settings, or Store Managers in the sidebar. Those pages are currently available only to the store owner.
## Related pages [#related-pages]
# Store Settings (/docs/features/store-settings)
The Store Settings page is where you manage the details that apply to your whole store, like its name and logo, plus default behavior across every product and vendor.
This page covers the **Store** tile on the Settings page. Other Settings tools have their own pages: [Discord Webhooks](/docs/features/discord-notifications), [Imports & Exports](/docs/features/imports-and-exports), [Customer Bans](/docs/features/customer-bans), [System Redelivery](/docs/features/system-redelivery), [Customer Page](/docs/features/customer-page), and [Audit Log](/docs/features/audit-log).
## Where to find Store Settings [#where-to-find-store-settings]
Open the Settings page at [stores.allomancy.com/settings](https://stores.allomancy.com/settings) and click the **Store** tile to open the **Store Settings** page at [stores.allomancy.com/settings/store](https://stores.allomancy.com/settings/store).
If your store has a logo set, a preview appears in the header to the right of the title. While you are typing a new logo UUID, the preview updates with a dashed pulsing outline so you can see the new texture before you save.
## Updating the store profile [#updating-the-store-profile]
The top of the page has two fields:
* **Store Name**: the visible name of your store.
* **Logo UUID**: a Second Life texture UUID for your store logo. The form accepts the standard SL UUID format with or without braces. Leave the field blank to clear the logo.
Use Setup marketplace when you are ready to connect Second Life Marketplace sales to Allomancy. The dialog walks you through the Marketplace **Automatic Notifications (ANS)** setup, gives you Allomancy's **ANS URL** for the Marketplace form, and stores the Marketplace **Notification Salt** on your store. After Marketplace sales start arriving, use [Marketplace Listings](/docs/features/marketplace-listings) to associate those listings with Allomancy products.
To save your changes, click Save
When set, your store logo is reused elsewhere in Allomancy. For example, gift cards handed out by a [Gift Card Delivery Unit](/docs/features/gift-card-delivery-units) inherit the registered store logo when no Custom texture is set on the unit.
## Setting default purchase behavior [#setting-default-purchase-behavior]
The **More settings** section below the profile form has three switches. Each one takes effect as you flip it:
* **Refund duplicate purchases**: when on, a customer who buys a product they already own is refunded automatically. Only products marked **Copy** are refunded.
* **Automatically use credits**: when on, customer store credit is used automatically on every purchase. The customer pays the full price at the vendor inworld, and Allomancy refunds the L$ equivalent of the credit balance that was used. If the credit balance covers the whole price, the entire L$ payment is refunded; if it covers only part, the refund matches the partial credit used. **Off by default.**
* **Allow gifts using store credit**: when on, customers may use their store credit or discounts when sending a gift purchase to another resident.
Two examples of **Automatically use credits** turned on. Both customers buy a L$30 product.
**Full coverage.** A customer with L$50 of store credit pays L$30 at the vendor and is refunded L$30 in full; their credit balance drops to L$20.
**Partial coverage.** A customer with L$10 of store credit pays L$30 at the vendor and is refunded L$10; their credit balance drops to L$0.
## Configuring inworld sales notifications [#configuring-inworld-sales-notifications]
The **Inworld sales notify** dropdown controls whether you receive sales notifications in your Second Life local chat:
| Option | What it sends |
| ------------ | ------------------------------------------------------------------------------------------- |
| **None** | No inworld chat notifications. |
| **Non free** | Notifications only for paid sales (skips L$0 sales like comps and gifts that cost nothing). |
| **All** | Notifications for every sale. |
Your selection takes effect right away.
## Sending a landmark with each purchase [#sending-a-landmark-with-each-purchase]
The **Landmark** picker selects a dispenser item that is delivered to customers along with their purchase. This is typically used for a landmark that points back to your store, but the field accepts any dispenser item.
To prevent spamming customers, the same item is delivered at most once every 24 hours per customer.
## More store-wide tools [#more-store-wide-tools]
Three buttons next to the toggles open dedicated dialogs:
* opens a dialog that configures bonus credit awarded on purchases.
* opens a discount dialog that applies to every product at once.
* opens a dialog with additional notification settings, including the Discord notification categories described in [Discord Notifications](/docs/features/discord-notifications).
Inside the Bonus credit dialog, **Bonus credit expiry (days)** sets how long bonus credit lasts after a customer earns it. Leave it empty and the credit never expires; otherwise enter a whole number of days from 1 to 3650. A single expiry covers both **Base bonus %** and **Group bonus %**, and clicking Save stores all three together. See [Store Credit](/docs/features/store-credit) for how expiring credit behaves.
## Permissions [#permissions]
Settings, including Store Settings, is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Subscriptions (/docs/features/subscriptions)
A subscription removes the 5% transaction fee Allomancy charges on each sale and, depending on the plan, unlocks additional features. Subscriptions are billed in weeks, and the current subscription plans remove the fee, starting with **Premium Stores** at L$580 per week.
## Comparing the plans [#comparing-the-plans]
| Plan | Price | What it adds |
| ------------------ | -------------- | ----------------------------------------------------------------------------------------------- |
| **Premium Stores** | L$580 / week | Removes the 5% transaction fee on sales. |
| **Premium Plus** | L$960 / week | Removes the 5% transaction fee and adds bot-related features, such as connecting a Corrade bot. |
| **Plus + Hosting** | L$1,300 / week | Includes Premium Plus features, with bot hosting handled by Allomancy. |
Plan details evolve as Allomancy adds more functionality. The plan cards on the Subscriptions page always show the most up-to-date feature list.
## Where to find Subscriptions [#where-to-find-subscriptions]
The Subscriptions page lives at [stores.allomancy.com/subscriptions](https://stores.allomancy.com/subscriptions). It shows the available plans in a grid below an optional **Current Subscription** card.
If you already have an active subscription, the matching plan card shows a "Your Current plan" badge in its top-right corner. Its bottom button changes from Subscribe to Add time while it is your current plan.
The **Current Subscription** card at the top of the page shows your active plan name, the expiration date, and how many days are left. The status badge reads **Active** when your subscription is still valid, or **Expired** in red when it has run out.
## Subscribing to a plan [#subscribing-to-a-plan]
### Pick a plan [#pick-a-plan]
Click Subscribe on the plan you want. The subscription dialog opens with the chosen plan.
### Choose how many weeks [#choose-how-many-weeks]
Type the number of weeks in **Subscription Duration (weeks)**. You can also click a preset: \~ 1 Month (4 weeks), \~ 6 Months (26 weeks), or \~ 1 Year (52 weeks). The dialog shows the running total and the per-week price.
### Send the payment [#send-the-payment]
To request the payment, click Send payment
The subscription payment goes through one of your rezzed vendors. Before clicking Send payment, make sure you have at least one vendor inworld with debit permissions and enough L$ in the avatar that owns it.
## Adding time to your current plan [#adding-time-to-your-current-plan]
To extend your current plan, click Add Time on the Current Subscription card or Add time on the highlighted plan card. The same subscription dialog opens, already targeting your current plan. Pick how many additional weeks to buy and send the payment as you would for a new subscription.
The Add Time button on the Current Subscription card is disabled while the subscription is in the Expired state. To resume an expired subscription, use Subscribe on the plan card instead.
## When a subscription expires [#when-a-subscription-expires]
If your subscription expires, your store keeps operating normally. Allomancy automatically switches you to the **5% commission** billing model, which charges 5% on each sale instead of a weekly subscription fee. To go back to a paid plan, subscribe again from this page.
This fallback is also covered on the [Pricing page](/docs/pricing).
## Who can manage subscriptions [#who-can-manage-subscriptions]
The Subscriptions page is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# System Redelivery (/docs/features/system-redelivery)
System Redelivery sends a fresh copy of the **Allomancy system box** to you inworld. Open the **Request system Redelivery** tile from [Settings](https://stores.allomancy.com/settings), or open [stores.allomancy.com/settings/redelivery](https://stores.allomancy.com/settings/redelivery). The page opens a confirmation dialog automatically: click Request Redelivery to send the box.
Other redelivery flows: use [Customers](/docs/features/customers) for a single customer's purchases, or [Product Tools](/docs/features/product-tools) for mass product redelivery.
# Tools (/docs/features/tools)
The Tools area collects small creator-focused utilities like avatar lookup and shape merging.
## Where to find Tools [#where-to-find-tools]
Click **Tools** in the sidebar to open the **Tools Dashboard**. The Tools Dashboard shows cards for the available tools.
## Shape Merger [#shape-merger]
The **Shape Merger** card opens the external [Shape Merger](https://shapemerge.allomancy.com/) utility.
Shape Merger combines two Second Life shapes by keeping the body from one shape and the head from the other. It helps when you change your head but want to keep your body's shape, or change your body but want to keep your head's shape, without copying shape values one by one.
## User Inspect [#user-inspect]
The **User Inspect** card opens a lookup page for checking a Second Life avatar. Enter a **Username** and search to see the avatar's profile picture, **Username**, **UUID**, **Born**, **Status**, and **Payment Status**.
The **UUID** row includes a copy-to-clipboard control. **Born** shows the avatar's birth date with a computed age. **Status** shows **Online** or **Offline**, and the profile picture border uses that same online state as a visual cue. **Payment Status** can show **No payment info**, **On file**, **Used**, or **Unknown**.
## Permissions [#permissions]
Tools is visible only to the store owner. Managers do not see it from a managed store. See [Store Managers](/docs/features/store-managers) for the full list of owner-only areas.
## Related pages [#related-pages]
# Vendors (/docs/features/vendors)
The Vendors page is where you look after every vendor object you have rezzed across Second Life. Use it to find a specific vendor, change its product or price, run a discount campaign across many vendors at once, configure your store's locator HUD, and adjust store-wide settings like vendor name prefixes.
If you are setting up a vendor for the first time, start with [Vendor Setup](/docs/quickstart/vendor-setup). This page covers the ongoing management that comes after your first sale.
The page lives at [stores.allomancy.com/vendors](https://stores.allomancy.com/vendors).
## Reviewing your vendors [#reviewing-your-vendors]
The list is sorted with the most recently created vendor first. Each row shows:
* **Name**: the vendor object's name in Second Life. With the region color indicator on, a small colored square also appears here.
* **Action**: opens this vendor's edit dialog.
* **Product**: the product this vendor is paired with.
* **Type**: an icon for the vendor type, with a tooltip on hover. The column shows **Default**, **Multi-menu vendor**, **Multi-vendor**, **Gacha vendor**, and **Payment Terminal** badges.
* **State**: a row of icons that highlight notable settings. See [State icons](#state-icons) below.
* **Version**: the inworld vendor's script version.
* **Region**: a link that opens the SLurl of the vendor in a new tab.
Three toggles next to the vendor list narrow the results:
* **Region color indicator**: adds a colored square next to vendors so you can group same-region rows visually.
* **Show only vendors with discount or price override**: hides any vendor that is selling at the unmodified product price.
* **Show only group-only vendors**: shows only vendors flagged as group-only.
### State icons [#state-icons]
| Icon meaning | When it appears |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| **Profit shares** | The vendor splits sales with collaborators. |
| **Vendor discount** | The vendor has an individual discount applied. The percentage appears on hover. |
| **Group discount** | The vendor has a group discount that differs from its regular discount. |
| **Price override** | The vendor sells at a custom price instead of the product's price. The amount appears on hover. |
| **Group only** | The vendor only sells to customers in the same active group as the vendor. |
| **Block credits** | Customers cannot pay this vendor with store credit. |
| **Tracking disabled** | Customers cannot find this vendor through the locator HUD or website. |
Hover any icon for the full tooltip and the specific values applied to that vendor.
## Editing a vendor [#editing-a-vendor]
Click on any row to open the **Vendor setup** dialog. The dialog has four numbered steps along the top: **Main settings**, **Discounts**, **Vendor Type**, and **Profit Shares**. You can move between steps freely, and Save is available from any step.
### Configuring main vendor settings [#configuring-main-vendor-settings]
* **Product**: the product paired with this vendor.
* **Block use of credits**: stop customers from paying with store credit at this vendor.
* **Only sell for group members**: when on, only residents whose active group matches the vendor's group can purchase.
* **Touch to acquire when price is L$0**: when the vendor price is L$0, customers receive the product by clicking the vendor instead of having to pay L$1. The vendor owner always sees the menu, so test from another account.
* **Trackable**: when on, customers can find this vendor through the locator HUD or website.
* **Override Price**: turn this on to sell at a custom L$ price instead of the product's price.
* **Apply image on vendor face**: paint the product image on a specific face of the vendor mesh. Face numbers run 0 through 7.
* **Click Action**: change what happens when a customer clicks the vendor. The dialog exposes two values plus an **Unchanged** option:
| Click Action | What it sets the vendor to |
| ------------ | ---------------------------------- |
| **Menu** | Open the vendor menu when clicked. |
| **Pay** | Open the pay dialog when clicked. |
### Setting discounts on the vendor [#setting-discounts-on-the-vendor]
The Discounts step has two percentage fields:
* **Discount**: a discount that always applies at this vendor.
* **Group Discount**: a discount that applies to customers in the same group as the vendor.
Both are independent of any product or category discount. Run a small test purchase if you need to confirm the final price after multiple discounts apply.
### Choosing the vendor type [#choosing-the-vendor-type]
Choose one of four vendor types from the dropdown:
* **Default**: a regular single-product vendor.
* **Multi-menu Vendor**: a vendor that browses multiple products. When you select this type, a **Vendor category** picker appears so you can limit which products show up on this vendor.
* **Gacha**: a vendor that pulls from a gacha you have configured. When you select this type, a **Gacha Selection** picker appears, with a shortcut to create a new gacha if you do not have one yet.
* **Payment Terminal**: a vendor that records custom inworld payments instead of selling a product. It records any amount a resident pays it inworld, shown on your [received payments](/docs/features/payments). It delivers no product and always accepts custom amounts, and discounts do not apply. Your product, price, and discount settings are kept but ignored while the type is selected, and switching the type back restores them.
The vendor type step needs vendor script version 1.20 or later. Older vendors show a "Please update your vendors" notice instead.
### Splitting profit on the vendor [#splitting-profit-on-the-vendor]
The Profit Shares step lets you assign per-vendor profit shares to other residents. Use it when this vendor's sales should split between collaborators.
To delete a vendor entirely, click at the bottom of the dialog. A confirmation warns you that the vendor will be deleted inworld as well.
## Running a bulk update across vendors [#running-a-bulk-update-across-vendors]
The Bulk Update button at the top of the page opens a single dialog that can change discounts, price overrides, credit blocking, click action, and tracking on a whole range of vendors at once. Each kind of change is opt-in:
* Toggle **Set Discounts** to update the regular and group discounts.
* Toggle **Set price override** to set a price across the matched vendors.
* Toggle **Set credit block** to enable or disable credit blocking.
* The **Click Action** and **Vendor Tracking** selects accept an **Unchanged** option so you can skip them.
Use the filters to choose which vendors get touched:
* **Filter by Product**: a partial product name match.
* **Filter by Region**: a Second Life region name.
* **Filter by UUIDs**: paste one or more vendor UUIDs separated by commas or whitespace. You can select your vendors inworld and click **Copy Keys** in the edit menu to grab them.
Click Update to run the update. With no filters set at all, the update touches **all** of your vendors.
A bulk update with no filter touches every vendor in your store. Read the confirmation prompt before clicking through.
## Configuring tracking and the locator HUD [#configuring-tracking-and-the-locator-hud]
The locator HUD is the inworld tool customers use to find a specific vendor in your store. Two things control how a vendor appears in it:
* **Trackable** on the vendor itself (under [Main settings](#configuring-main-vendor-settings)). When off, the vendor is hidden from the HUD and from the website locator.
* **Vendor Tracking** at the store level. Click at the top of the Vendors page to open it. The dialog lets you pick custom **Target** and **Arrow** texture UUIDs so the HUD matches your store's look. A **Download template** link supplies a base file. Click Request HUD to receive a fresh copy of the locator HUD inworld.
## Configuring store-wide vendor settings [#configuring-store-wide-vendor-settings]
Click at the top of the Vendors page to open **Vendor Settings**, which apply to every vendor in your store:
* **Custom payment field**: when on, customers can pay any value at a vendor. If they have store credit, the missing amount is deducted from their credit. Otherwise the purchase is rejected.
* **Prefix**: text shown before each vendor's name.
* **Suffix**: text shown after each vendor's name.
## Permissions [#permissions]
Managing vendors needs the **Manage Vendors** permission. Store owners always have it. To grant it to a staff member, see [Store Managers](/docs/features/store-managers).
## Related pages [#related-pages]
# Account Creation (/docs/quickstart/account-creation)
First step: Set up the bridge between your Allomancy account and your Second Life avatar. Once the two are linked, Allomancy can send you the inworld tools you need for the rest of setup.
### Sign up [#sign-up]
Visit [Allomancy Stores](https://stores.allomancy.com) and create your account.
### Verify your email [#verify-your-email]
Open the verification email in your inbox and follow the link to confirm your address. After that, sign in to Allomancy Stores.
### Link your Second Life account [#link-your-second-life-account]
Enter your Second Life username in the form that appears on the website. Allomancy sends a link to you in Second Life chat. Click that link inworld to finish pairing your avatar with your store account.
### Receive your inworld package [#receive-your-inworld-package]
After your account is linked, you automatically receive an inworld package with the Allomancy dispenser, vendor, redelivery terminal, blog terminal, and related gadgets. Keep those objects handy, because the quickstart uses them to deliver products, take payments, and support customers.
## Next [#next]
# Dispenser Setup (/docs/quickstart/dispenser-setup)
The dispenser is the inworld object that delivers your boxed items to customers. Before you list a product, put the boxed item inside a rezzed dispenser so Allomancy has something to send.
## Loading items [#loading-items]
1. Rez a copy of the dispenser inworld.
2. Right-click it and choose **Edit**.
3. In the build window, open the **Contents** tab.
4. Drag the boxed items you want to sell from your inventory into the dispenser's contents.
## Keeping deliveries reliable [#keeping-deliveries-reliable]
The dispenser must stay rezzed for deliveries to work. You can keep as many dispensers rezzed as you like.
Running multiple dispensers in different regions helps protect deliveries if one region is offline or having trouble.
## Next [#next]
# Quickstart (/docs/quickstart)
Allomancy helps Second Life creators run their store. Your inworld objects handle delivery and sales, but the day-to-day work lives on the web: listing products, sending payments, managing bloggers, sending and receiving IMs, and publishing customer news.
Start here if you are setting up a store for the first time. These four steps take you from a new account to a working inworld vendor that customers can purchase from.
## What's next [#whats-next]
Once your first product is live, you can choose how deep you want to go. Allomancy can send sales notifications to Discord, track and send payments, manage blogger access, list products in batches, and publish customer news from the website.
# Product Listing (/docs/quickstart/product-listing)
Once your dispenser holds an item, create a product listing for it on the website. The listing controls the product name, price, image, permissions, blogger access, and any profit share for that item.
### Open the products page [#open-the-products-page]
Go to the [products page](https://stores.allomancy.com/products) and click at the top of the page.
### Pick the item [#pick-the-item]
In the **Item to be delivered** field, start typing the item's name. A dropdown appears with matching contents from your dispenser. Select the item you want this listing to deliver.
### Set the price and details [#set-the-price-and-details]
Enter the product price in L$. The product name fills in from the item name, but you can edit it if the customer-facing name should be different. The **Image UUID** field is optional, but it is recommended because the image appears on the store redelivery page and on blogger pages.
### Configure permissions [#configure-permissions]
Switch to the **Extra Config** tab if the product needs special handling. Turn on **Bloggable** to make the product available for your bloggers to retrieve. Items are marked **Copy** by default, which allows customers to request redeliveries. If you sell a no-copy item, uncheck **Copy** before saving.
Permission checkboxes tell Allomancy how the product should be treated. They do not change the actual permissions of the item inside the dispenser, so make sure the inworld item is configured correctly too.
### Optional: profit share [#optional-profit-share]
Open the **Profit Share** tab if this product should split earnings with collaborators. Add the residents who should receive part of each sale and set each person's share.
### Save the listing [#save-the-listing]
To list the product, click Save
## Next [#next]
# Vendor Setup (/docs/quickstart/vendor-setup)
The vendor is the inworld object customers pay when they buy from you. Each vendor points to one product, and Allomancy uses that pairing to set the vendor price and deliver the right item.
### Rez the vendor and choose a setup path [#rez-the-vendor-and-choose-a-setup-path]
Place the vendor in your store and grant debit permissions when Second Life asks. Click the vendor to open the setup dialog. Choose **Quick Setup** to pair the vendor inworld, or choose **Web setup** to receive a setup link in local chat and finish pairing on the website.
### Pair it with a product [#pair-it-with-a-product]
Two ways to do that:
* **Quick Setup**: type the product name, or part of it, into the inworld text input. Allomancy matches the closest listed product and pairs the vendor with it. Very handy if all you need is to associate the product.
* **Web setup**: open the setup link from local chat. In the setup form on the website, start typing your product's name in the **Product** field. Pick the product from the dropdown, then click Save so the vendor updates with the product's current price.
### Test the vendor [#test-the-vendor]
Back inworld, pay the vendor to make a test purchase. You should receive the product the same way a customer would.
## What's next [#whats-next]
# Applications (/docs/features/bloggers/applications)
Applications are the first step in onboarding a new blogger. From the merchant side you control whether your store is accepting applicants, what they see on the application form, and how you respond to each application. From the blogger side, applicants find your store on [bloggers.allomancy.com](https://bloggers.allomancy.com), fill out the form, and track their submitted applications until you review them.
## Opening applications [#opening-applications]
The two switches at the top of the [Blog Dashboard](https://stores.allomancy.com/blog) control whether new bloggers can find and apply to your store:
* **Applications are open / closed**: when open, bloggers can submit an application to your store. The label appears in green when applications are open and in red when they are closed.
* **Listed for bloggers**: when enabled, your store is listed on [bloggers.allomancy.com](https://bloggers.allomancy.com) so bloggers can discover it. This switch is disabled while applications are closed.
While applications are open, the Blog Dashboard also surfaces a copyable **Applications URL** in the form `https://bloggers.allomancy.com/stores/view/{storeId}`. Share this link directly so a specific blogger lands on your application form without browsing the public list.
## Configuring the application form [#configuring-the-application-form]
Open the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Settings** tile, and switch to the **Application** tab to configure what applicants see.
The tab has three sections:
* **About (Appears on application page)**: a free-form field. The text shows up under the **About** heading on your store's application page on [bloggers.allomancy.com](https://bloggers.allomancy.com).
* **Application Cooldown (Days)**: the waiting period in days after rejection before a blogger can reapply.
* **Application questions**: a list of up to five free-form questions that bloggers must answer when applying. Use Add Question to add a new question, or beside a question to remove it.
Click Save All Settings at the bottom of the page to save the Application tab and the Settings tab together.
## How a blogger applies [#how-a-blogger-applies]
A blogger logs into [bloggers.allomancy.com](https://bloggers.allomancy.com) and opens **Find Stores** to browse the **Available stores** list. Stores you have set as **Listed for bloggers** appear here. The blogger can search the list and click a store card to open its application page.
The application page shows your logo, your **About** text, your custom questions, and an **Info/details about you** field where the blogger writes about themselves.
Submitting the application returns the blogger to the **Available stores** list.
A blogger can track submitted applications on **Your sent applications**. The **Status** icon shows whether each application is pending, accepted, or rejected: for pending, for accepted, and for rejected. While an application is still pending, lets the blogger withdraw it before review.
## Reviewing applications [#reviewing-applications]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Applications** tile to open the **Applications** page. The table lists submitted applications.
Use the **Show reviewed** toggle at the top to switch between pending applications (default) and applications you have already accepted or rejected.
The table has these columns:
* **Username**: the applicant's Second Life profile picture and username.
* **Social**: social-link icons from the applicant's blogger profile.
* **Info**: the applicant's free-form Info text, truncated.
* **Date**: when the application was submitted.
* **Answers**: the count of answered questions.
* **Status**: for pending applications, for accepted, and for rejected.
* **Action**: opens the application for review.
## Accepting or rejecting an application [#accepting-or-rejecting-an-application]
Click on any row to open the application detail dialog.
### Review the applicant [#review-the-applicant]
The dialog shows the applicant's username and the date they applied at the top, followed by three sections:
* **Social Links**: the applicant's social-network links.
* **Information**: the applicant's free-form Info text.
* **Application Answers**: the answers to your custom questions, listed under each question.
### Add an optional review comment [#add-an-optional-review-comment]
Use **Review Comment (Optional)** to record a comment for your decision.
### Accept or reject [#accept-or-reject]
Click Accept to add the applicant to your bloggers list, or Reject to refuse the application.
After you accept or reject an application, the comment field, **Accept**, and **Reject** are disabled. The previously entered comment appears but you cannot edit it.
## What the blogger sees after a decision [#what-the-blogger-sees-after-a-decision]
On the blogger's **"Your sent applications"** table:
* An accepted application shows the accepted icon. Accepted bloggers can use the store-specific blogger workflows available from the blogger-facing site.
* A rejected application shows the rejected icon.
* Only pending applications can be withdrawn by the blogger.
Application review comments are kept private to your team.
## Permissions [#permissions]
The Applications page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Coupons (/docs/features/bloggers/coupons)
The Coupons section of the Blog Dashboard handles two related things: store-wide settings that bloggers can use to generate their own coupon codes, and a list of individual coupons you can add and edit yourself. Bloggers see and manage their own coupon code on the blogger-facing site.
## Configuring store-wide blogger coupons [#configuring-store-wide-blogger-coupons]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Coupons** tile to open the **Coupons Management** page at [stores.allomancy.com/blog/coupons](https://stores.allomancy.com/blog/coupons). Switch to the **Blogger Coupon Settings** tab to configure the store-wide settings.
The tab has these controls:
* **Enable coupon creation for all bloggers**: a toggle that controls whether bloggers can create their own coupon codes. The change takes effect right away.
* **Blogger commission**: the percentage paid to the blogger for each sale that uses their coupon.
* **Customer discount**: the percentage discount the customer receives when using the coupon.
These settings shape how blogger coupons work: bloggers can generate their own codes that use the commission and discount you set above. When a customer uses a blogger's coupon, the blogger receives the commission as L$, and the customer's discount is applied as store credit.
Use Save to save changes to **Blogger commission** and **Customer discount**.
When a customer uses a blogger coupon, Allomancy sends the blogger's commission L$ at the moment of sale. The commission appears on your **Sent Payments** tab with the **Source** label **Coupon** and a Message in the form `Coupon commission 'COUPON_CODE'`.
The customer's coupon-discount portion is added to the customer's store credit balance. For how that store credit interacts with the customer's other discounts at purchase time, see the [Loyalty discounts](/docs/features/customers#loyalty-discounts) section on the Customers page.
## How a blogger uses their coupon [#how-a-blogger-uses-their-coupon]
A blogger opens **Coupons** for a store on [bloggers.allomancy.com](https://bloggers.allomancy.com). Customers redeem the coupon by clicking the discount button on a vendor and entering the code, and the blogger receives their L$ commission for each sale that uses it.
What the blogger sees next depends on the store-wide settings:
* If you have not enabled blogger coupons for this store, the page shows that coupon creation is disabled.
* If blogger coupons are enabled but the blogger has not created a code yet, the page shows a **Generate** button.
* If a code already exists, the page shows the code with a copy-to-clipboard button, the blogger's profit percentage, and the customer discount percentage.
The dialog asks the blogger to enter a coupon code and save it.
## Reviewing your coupons [#reviewing-your-coupons]
Switch to the **Coupons Table** tab on **Coupons Management** to see individual coupons for your store. Add Coupon at the top opens the add dialog described below.
The table has these columns:
* **Coupon Owner**: the username associated with the coupon.
* **Coupon Code**: the code, in monospace.
* **Discount**: the customer discount.
* **Profit**: the owner profit.
* **Created**: the timestamp the coupon was created.
* **Blogger Sync**: a group icon. The icon appears in blue when the coupon is synced with the store-wide blogger coupon settings, and in a muted color otherwise.
* **Status**: a colored text badge, either **Active** (green) or **Inactive** (muted).
* **Actions**: edits the coupon, and deletes it.
## Adding or editing a coupon [#adding-or-editing-a-coupon]
Click Add Coupon at the top of the **Coupons Table** tab, or next to a row, to open the coupon dialog.
### Set the owner [#set-the-owner]
Enter **Owner Username**. You cannot change it after the coupon is created.
### Set the code [#set-the-code]
Enter **Code**.
### Choose how the coupon's percentages are set [#choose-how-the-coupons-percentages-are-set]
Use the **Bloggers Sync** checkbox to choose between the two modes.
When **Bloggers Sync** is on, **Active**, **Discount**, and **Owner Profit** are hidden, and the coupon uses the store-wide blogger coupon settings shown on the settings tab. When it is off, those fields appear on the coupon dialog.
### Set custom values, if Bloggers Sync is off [#set-custom-values-if-bloggers-sync-is-off]
When **Bloggers Sync** is off:
* **Active** checkbox marks the coupon as active or inactive. New coupons default to active.
* **Discount** is a percentage; new coupons default to 10.
* **Owner Profit** is a percentage; new coupons default to 0.
### Save [#save]
Click Add Coupon when adding a new coupon, or Save Changes when editing.
## Deleting a coupon [#deleting-a-coupon]
To delete a coupon, click on its row.
## Permissions [#permissions]
The Coupons page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Deadlines (/docs/features/bloggers/deadlines)
Allomancy shows product deadlines for bloggers in both the merchant Blog Dashboard and the blogger-facing site. This page covers where deadlines show up on the merchant Blog Dashboard, what bloggers see on their side, and how extension requests are reviewed.
## Where deadlines come from [#where-deadlines-come-from]
Deadline-related settings live in the **Posts / Deadlines** card on the [Blog Settings page](https://stores.allomancy.com/blog/settings). The visible deadline-related fields are:
* **Default Deadline (Days)**: how many days after requesting a product the blogger has to post it.
* **Required Posts Mode**: a dropdown with **None** or **Monthly**.
* **Number of posts required**: shown when **Required Posts Mode** is not **None**.
Use the [Blog Settings page](https://stores.allomancy.com/blog/settings) to change these values; the [Blogger Management overview](/docs/features/blogger-management) introduces the rest of the store-wide settings.
## Reviewing pending deadlines [#reviewing-pending-deadlines]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **View Deadlines** tile to open the **Deadlines** page at [stores.allomancy.com/blog/deadlines](https://stores.allomancy.com/blog/deadlines).
The table shows product deadlines for your bloggers and has these columns:
* **Blogger**: the blogger's username.
* **Product**: the product name.
* **Retrieved**: when the blogger retrieved the product.
* **Deadline**: the deadline by which the blogger is expected to post.
The Deadlines page is read-only. There are no per-row actions on this page. To reset deadlines for a single blogger, see **Reset Deadlines** in [Managing Bloggers](/docs/features/bloggers/managing-bloggers). To review a blogger's deadline-extension request, use the **Extension requests** tile described below.
## What the blogger sees [#what-the-blogger-sees]
The blogger landing page on [bloggers.allomancy.com](https://bloggers.allomancy.com) includes two cards relevant to deadlines.
### Next Deadlines [#next-deadlines]
The **Next Deadlines** card lists upcoming product deadlines, with these columns:
* **Expiration**: the deadline date with hover-tooltip detail. Expiration dates within 5 days appear in red.
* **Store**: the store the product belongs to.
* **Product**: the product name.
* **Action**: a navigation icon that takes the blogger to that store's post-creation page.
When the blogger has no upcoming deadlines, the card says there are no upcoming deadlines.
### Monthly Requirements [#monthly-requirements]
The **Monthly Requirements** card lists stores with monthly posting requirements. Its columns are:
* **Store**: the store name.
* **Required posts**: shows the number of products the blogger still needs to post for the store, with a hover tooltip explaining the monthly count and how many they have already posted.
* **Action**: the same navigation icon, taking the blogger to that store's post-creation page.
When the blogger has met their monthly requirements, the card says there are no monthly requirements left.
## Requesting an extension [#requesting-an-extension]
A blogger can request a deadline extension for selected products from the post-creation page on [bloggers.allomancy.com](https://bloggers.allomancy.com). With at least one product selected, the **Request extension** button opens the request dialog.
In the dialog, the blogger reviews the selected products, adds a **Reason**, optionally enters **Extra days** to ask for time beyond the default deadline, and clicks **Request** to submit. Accepting the request resets the product deadline, with optional extra days added. Only one extension request can be pending at a time per blogger.
## Reviewing extension requests [#reviewing-extension-requests]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Extension requests** tile to open the **Extension Requests** page at [stores.allomancy.com/blog/extension-requests](https://stores.allomancy.com/blog/extension-requests).
A toggle at the top, **Show Reviewed**, reveals previously reviewed requests when turned on. While **Show Reviewed** is on, the eye action button is disabled, so you can inspect reviewed rows in the list but not open them in the review dialog from this page.
The table has these columns:
* **Blogger**: the blogger's username.
* **Extra Days**: the number of extra days requested.
* **Reason**: the blogger's reason text, truncated.
* **Products**: thumbnail images for each product in the request, with hover tooltips showing the product names.
* **Status**: plain text, one of **"Accepted"**, **"Rejected"**, or **"Pending"**.
* **Action**: opens the request for review.
### Reviewing an extension request [#reviewing-an-extension-request]
Click to open the extension review dialog. The dialog shows the requested change and the blogger's reason.
Click Deny to reject the request, or Accept to grant the extension.
This flow uses **Deny**, not Reject. The wording differs from the application and post review flows but the meaning is the same: the request is not granted.
## Resetting deadlines per blogger [#resetting-deadlines-per-blogger]
To reset all pending post deadlines for a single blogger without going through an extension request, open that blogger's detail page and use the **Reset Deadlines** action. See [Managing Bloggers](/docs/features/bloggers/managing-bloggers) for the per-blogger actions.
Vacation requests are documented separately; see [Vacations](/docs/features/bloggers/vacations).
## Permissions [#permissions]
The Deadlines and Extension requests pages live under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Managing Bloggers (/docs/features/bloggers/managing-bloggers)
Once a blogger is accepted into your program, you manage them from the **Bloggers** tile on the [Blog Dashboard](https://stores.allomancy.com/blog). This page covers the bloggers list, the per-blogger detail page, and the actions you can take on each blogger.
## Reviewing your bloggers [#reviewing-your-bloggers]
Open the [Blog Dashboard](https://stores.allomancy.com/blog) and click the **Bloggers** tile to open the **Bloggers** page at [stores.allomancy.com/blog/bloggers](https://stores.allomancy.com/blog/bloggers).
The table has these columns:
* **Blogger**: the blogger's Second Life username. The text turns red when the blogger is **Suspended**. If the blogger is currently on vacation, an inline palm-tree icon appears next to the username.
* **Vip**: a status icon that uses when the blogger is marked VIP and when they are not.
* **Status**: **Active** for unsuspended bloggers, or **Suspended** for suspended bloggers.
* **Social**: social-link icons from the blogger's profile.
* **Products**: the count of products this blogger has retrieved.
* **Approved Posts**: the count of posts you have approved.
* **Pending Posts**: the count of posts awaiting review.
* **Joined**: when the blogger joined your program.
* **Action**: opens the blogger's detail page.
## Adding a blogger manually [#adding-a-blogger-manually]
Click Add New Blogger above the bloggers list, enter the blogger's Second Life **Username**, optionally tick **VIP**, and click Add Blogger
## Opening a blogger's detail page [#opening-a-bloggers-detail-page]
Click in the **Action** column of any row to open that blogger's detail page. The page header shows the blogger's Second Life profile picture and username.
The detail page has two tabs: **Profile** and **Products**.
### Profile tab [#profile-tab]
The **Profile** tab is organized into four sections:
* **Description**: the blogger's free-form description, taken from their own blogger profile.
* **Badges**: three pill badges that reflect blogger state, described below.
* **Statistics**: a small grid showing the blogger's **Products**, **Approved Posts**, **Pending Posts**, and **Joined** values.
* **Social Links**: the blogger's social-link icons.
From the Profile tab you can end an active vacation, reset the blogger's deadlines, or remove the blogger from your program, each covered in the sections that follow.
### Products tab [#products-tab]
The **Products** tab shows the products this blogger has retrieved from your store. The table includes the product, retrieval date, and two status columns: **Posted** uses when no post has been submitted yet, when a post has been submitted, and for the negative state; **Accepted** uses the same icon set for review status.
## Toggling VIP and Suspended [#toggling-vip-and-suspended]
The **VIP** and **Suspended** badges on the Profile tab are clickable. Each click toggles the corresponding flag immediately, with no save step.
* **VIP**: click to mark the blogger as VIP, click again to clear the flag. The badge uses a filled style when VIP is on and a muted outline when off.
* **Suspended**: click to suspend the blogger, click again to lift the suspension. The badge is red when suspended and a muted outline when not.
The **On Vacation** badge appears next to **VIP** and **Suspended** while the blogger is currently on vacation. It is informational only and is not clickable.
**VIP** bloggers are exempt from product deadlines and monthly post requirements. They can retrieve and post products on their own schedule without risk of suspension for missing deadlines. Many stores apply VIP to friends or trusted bloggers.
**Suspended** bloggers cannot retrieve products or submit posts. Only the store owner or a manager with the **Manage Bloggers** permission can lift the suspension. Lifting a suspension resets all of the blogger's product deadlines to the **Default Deadline (Days)** value configured on Blog Settings.
## Ending an active vacation [#ending-an-active-vacation]
When the blogger is on vacation, click End vacation under the badges and statistics to end it.
## Resetting deadlines [#resetting-deadlines]
To reset pending post deadlines for the blogger, click Reset Deadlines, then click Reset Deadlines to confirm.
## Removing a blogger [#removing-a-blogger]
To remove the blogger from your program, click Remove Blogger, then click Remove Blogger to confirm.
Posts the blogger has already submitted remain visible on your **Posts** page after removal.
## What the blogger sees [#what-the-blogger-sees]
Bloggers manage their own description and social links from their profile on the blogger-facing site. The stores they belong to appear under their **Your stores** section on [bloggers.allomancy.com](https://bloggers.allomancy.com).
## Permissions [#permissions]
The Bloggers page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Messages (/docs/features/bloggers/messages)
The **Messages** tile lets you compose a message in the **Message for your bloggers** dialog, review past messages you have sent, and delete messages from the Messages page. Bloggers see those messages on the blogger-facing site.
## Sending a message [#sending-a-message]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Messages** tile to open the **Messages** page at [stores.allomancy.com/blog/messages](https://stores.allomancy.com/blog/messages).
Click New Message at the top of the page, type the body in **Message for your bloggers**, and click Send to send it.
## Reviewing your messages [#reviewing-your-messages]
The Messages page lists messages you have sent, with these columns:
* **Message**: the message body.
* **Sender**: the username of the sender.
* **Date**: when the message was sent.
* **Action**: opens the delete confirmation described below.
## Deleting a message [#deleting-a-message]
To delete a message, click on its row.
## What the blogger sees [#what-the-blogger-sees]
On the blogger-facing site, each store's **Messages** page shows the messages you sent, including the date, sender (either the store owner or a manager).
## Permissions [#permissions]
The Messages page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Moderation (/docs/features/bloggers/moderation)
The Blog Dashboard includes a **Bans** tile for managing blogger bans. Other moderation actions, such as marking a blogger as **Suspended** or using **Remove Blogger**, live on the per-blogger detail page in [Managing Bloggers](/docs/features/bloggers/managing-bloggers).
## Where to find Blog Bans [#where-to-find-blog-bans]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Bans** tile to open the **Bans** page at [stores.allomancy.com/blog/bans](https://stores.allomancy.com/blog/bans).
The Blog **Bans** page lets you add and manage bans by blogger username. This is separate from customer bans.
The table has these columns:
* **Blogger**: the blogger's profile picture and Second Life username.
* **Reason**: the reason you entered when you created the ban.
* **Banned Date**: when the ban was created.
* **Action**: opens the lift-ban confirmation described below.
## Banning a blogger [#banning-a-blogger]
To open the **Ban Blogger** dialog, click New Ban above the list. Enter the blogger's Second Life **Username**, optionally add a **Reason**, and click Ban to submit.
## Lifting a ban [#lifting-a-ban]
To lift the ban, click on its row.
## Blog Bans vs Customer Bans [#blog-bans-vs-customer-bans]
Blog Bans are managed under the Blog Dashboard and are separate from Customer Bans under Settings. See [Customer Bans](/docs/features/customer-bans) for the customer-side ban list.
## Other moderation actions [#other-moderation-actions]
For per-blogger actions that do not use this page, see [Managing Bloggers](/docs/features/bloggers/managing-bloggers):
* The **Suspended** badge on a blogger's profile.
* The **Remove Blogger** action on a blogger's detail page.
## Permissions [#permissions]
The Bans page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Posts (/docs/features/bloggers/posts)
Posts are the deliverable of the blogger program. Bloggers submit a post about one or more products from your store, and you review and approve or reject each one from the **Posts** tile on the [Blog Dashboard](https://stores.allomancy.com/blog).
## Submitting a post [#submitting-a-post]
A blogger creates a post on [bloggers.allomancy.com](https://bloggers.allomancy.com) from a store's post-creation page. The page shows the products available for posting and lets the blogger pick one or more.
Above the product grid, the blogger can switch between two views with the **Unposted** / **Posted** toggle. Selected products show a red **Selected** badge, and a **Selected: {'{n}'}** counter appears above the grid.
For non-VIP stores, unposted product cards display a **"Deadline: MMM dd, yyyy"** description under the product name. For VIP stores, no deadline appears on the unposted product cards.
When at least one product is selected, the blogger clicks **Post** to open the post submission dialog.
In the dialog, the blogger adds one to three URLs (use **Add** to add another URL field, **Remove** to drop the last), optionally writes free-form details in **Info**, and clicks **Post** to submit.
## Updating a pending post [#updating-a-pending-post]
A blogger can update a post they have already sent, but only while it is still pending review. From the blogger's **Posts** page for a store, the **Action** column shows an **Update** button that is disabled once you have reviewed the post.
Clicking **Update** opens the same submission dialog used for new posts. The URL fields and **Info** field are pre-filled with the existing values.
## What the blogger sees in their Posts list [#what-the-blogger-sees-in-their-posts-list]
The blogger's **Posts** page for a store shows posts they have submitted to that store. The table has these columns:
* **Status**: for pending posts, for accepted posts, and for rejected posts.
* **Products**: the count of products in the post, with a hover tooltip listing the product names.
* **Links**: a row of social-link icons, one per URL on the post. Hovering an icon shows the full URL.
* **Comment**: a preview of the merchant's review comment, truncated. Click the cell to open a comment dialog described below.
* **Date**: when the post was submitted.
* **Action**: the **Update** button, disabled when the post has been reviewed.
Clicking the **Comment** cell opens a dialog titled **Post comment** with two stacked sections:
* **Your post info:** the post description, with URLs turned into clickable links.
* **Store comment:** the merchant's review comment, with URLs turned into clickable links.
## Reviewing pending posts [#reviewing-pending-posts]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Posts** tile to open the **Posts** page at [stores.allomancy.com/blog/posts](https://stores.allomancy.com/blog/posts).
The list shows pending, accepted, and rejected posts together; the **Status** column distinguishes them.
The table has these columns:
* **Blogger**: the blogger's username.
* **Description**: the blogger's free-form Info text, truncated.
* **Status**: for pending posts, for accepted posts, and for rejected posts.
* **Created Date**: when the post was submitted.
* **URLs**: social-link icons for each post URL.
* **Products**: thumbnail images for each product in the post, with a hover tooltip showing the product name.
* **Action**: opens the post for review.
## Reviewing a post [#reviewing-a-post]
Click on any row to open the post review dialog.
The dialog is organized into sections:
* **Description**: the blogger's full Info text in a scrollable box.
* **Post URLs**: shown only when the post has at least one URL, rendered as social-link icons in a dashed box.
* **Products**: shown only when the post has at least one product, listed by name.
* **Review Comment (optional)**: a field for your comment.
Click Reject to reject the post, or Accept to approve it.
Reopening a reviewed post shows the previous comment in the comment field. Clicking **Accept** or **Reject** again updates the post's decision; the blogger receives an inworld notecard with the new decision, using the same notecard format as application-decision notifications.
## What the blogger sees after a decision [#what-the-blogger-sees-after-a-decision]
On the blogger's **Posts** table, an accepted post shows the accepted icon and a rejected post shows the rejected icon. The **Update** button is disabled for that post.
If you entered a review comment, the blogger sees it in the **Store comment:** section of the **Post comment** dialog when they click that post's **Comment** cell on their **Posts** page.
## Permissions [#permissions]
The Posts page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]
# Vacations (/docs/features/bloggers/vacations)
A blogger can request a vacation period from your store. You review pending requests from the **Vacation requests** tile on the [Blog Dashboard](https://stores.allomancy.com/blog) and accept or decline each one. Vacation status for accepted requests appears alongside other per-blogger information in [Managing Bloggers](/docs/features/bloggers/managing-bloggers).
## Requesting a vacation [#requesting-a-vacation]
A blogger requests a vacation from a store on [bloggers.allomancy.com](https://bloggers.allomancy.com), from that store's **Store options** page. The action button reads **Request vacation**.
### Choose a date range [#choose-a-date-range]
The dialog asks the blogger to choose a vacation date range using a date range picker. The default range starts today and ends one month from today, and the blogger can adjust both ends.
### Add a reason [#add-a-reason]
Use **Info / Reason** to explain the request.
### Send the request [#send-the-request]
Click **Request** to submit. **Request** is enabled once the date range is valid.
## Reviewing vacation requests [#reviewing-vacation-requests]
In the [Blog Dashboard](https://stores.allomancy.com/blog), click the **Vacation requests** tile to open the **Vacation Requests** page at [stores.allomancy.com/blog/vacation-requests](https://stores.allomancy.com/blog/vacation-requests).
The list shows pending, accepted, and rejected requests together; the **Status** column distinguishes them.
The table has these columns:
* **Blogger**: the blogger's username.
* **Start Date**: the requested start date.
* **End Date**: the requested end date.
* **Reason**: the blogger's free-form reason text.
* **Status**: plain text, one of **"Accepted"**, **"Rejected"**, or **"Pending"**.
* **Action**: opens pending requests for review.
### Reviewing an individual request [#reviewing-an-individual-request]
Click on a pending request to open the vacation review dialog.
The body has two parts:
* A **Start Date** and **End Date** block showing the requested dates, formatted like `MMM d, yyyy`.
* A **Reason** label above a scrollable, whitespace-preserving block with the blogger's reason text.
Click Decline to decline the request, or Accept to accept it.
This flow uses **Decline**. Other Blog review flows use **Reject** (Applications, Posts) or **Deny** (Extension requests). The terminology differs across flows but the meaning is the same: the request is not granted.
## After a decision [#after-a-decision]
An accepted vacation appears in the blogger list and on the blogger's profile, as documented in [Managing Bloggers](/docs/features/bloggers/managing-bloggers):
* The bloggers list shows a vacation indicator next to the blogger's username while the vacation is currently active.
* The blogger's detail page shows an **On Vacation** badge while the vacation is currently active.
* The **End vacation** action on the detail page lets you end the vacation early.
## Permissions [#permissions]
The Vacation requests page lives under the **Blog Dashboard**, which is gated by the **Manage Bloggers** permission. To grant or restrict that permission, see [Store Managers](/docs/features/store-managers). For the broader blogger program context, see [Blogger Management](/docs/features/blogger-management).
## Related pages [#related-pages]