Overview
Pick the Custom display type in DisplayCache when none of the built-in types (Birthdays, Events, Milestones, Volunteers) match what you want to show. You write a TouchPoint Python script that returns rows as JSON, and DisplayCache renders them on your screens using the same Cards / Table / Bulletin layouts as the built-in types.
- Save a Python script in TouchPoint under Admin → Special Content → Python.
- Make it print a JSON array between
<!--DC_START-->and<!--DC_END-->markers. - In DisplayCache, set the display type to Custom, paste the script name, click Detect from Script, then Save & Publish.
1. Install your script in TouchPoint
- Sign in to TouchPoint with an account that has both the
DeveloperandAPIOnlyroles. (See the TouchPoint setup guide if you don't have them yet.) - Go to Admin → Special Content → Python.
- Click New and give your script a name without spaces — for example,
DisplayCacheBuildingUse. This name is what you'll paste into the Custom display in DisplayCache. - Paste your script body (see the example below for a starting point).
- Click Save. TouchPoint will compile the script; fix any syntax errors it reports.
That's it — you don't have to expose the script publicly or share a URL. DisplayCache calls it through the same authenticated /PythonAPI endpoint the connection used during setup.
2. Output format
Your script must print a JSON array (one row per record) wrapped in DisplayCache markers so the integration can extract it cleanly from TouchPoint's HTML response.
print("<!--DC_START-->")
print(json.dumps(rows))
print("<!--DC_END-->")
Each row is a JSON object. Field names become column keys; you can use any keys you like. Examples that work well on screen:
[
{
"name": "Sanctuary",
"next_event": "Sunday Service",
"starts_at": "Sun 10:00 AM",
"status": "Open"
},
{
"name": "Fellowship Hall",
"next_event": "Wednesday Dinner",
"starts_at": "Wed 6:00 PM",
"status": "Reserved"
}
]
error key — DisplayCache will surface it in the admin instead of failing silently.
print("<!--DC_START-->")
print(json.dumps({"error": "Permission denied"}))
print("<!--DC_END-->")
3. Reading parameters
DisplayCache passes any parameters you configure on the display (under Parameters on the Custom display card) into your script as query-string arguments. Inside TouchPoint they're available on the Data object.
days = int(Data.days) if hasattr(Data, 'days') and Data.days else 7
org_id = Data.oid if hasattr(Data, 'oid') and Data.oid else None
limit = int(Data.limit) if hasattr(Data, 'limit') and Data.limit else 50
Two important rules:
- Always check
hasattr(Data, 'foo')before readingData.foo— the attribute is missing if the user didn't set it. - Everything is a string when it arrives, so wrap numbers with
int(...)orfloat(...).
To add a parameter in DisplayCache, open the Custom display, click + Parameter, and enter a name (matching the attribute you read in the script) plus a value. Common parameters used by the built-in scripts:
| Name | Typical use |
|---|---|
days | Number of days ahead to look |
oid | TouchPoint Involvement / Organization ID |
search_name | Search Builder saved-search name |
limit | Maximum rows to return |
4. Detect columns
Once your script is saved and your parameters are set, click Detect from Script on the Custom display card. DisplayCache will run the script against your live TouchPoint data, look at the first row, and auto-populate the Columns table with every field it finds.
The Columns table controls which fields show on screen and what labels they use:
- Key — must match a field name in your JSON output (e.g.
starts_at). - Label — the human-friendly header shown on the Table format and as a prefix on Cards.
- Order — drag to reorder; delete any column you don't want to display.
You can also add columns manually if your dataset is sparse (some rows missing certain fields) so they don't get skipped by detection.
5. Example: Building usage this week
Here's a complete, runnable script that lists eSPACE-style building reservations directly from TouchPoint's Meetings table for the next 7 days. Save it in TouchPoint as DisplayCacheBuildingUse, then enter that same name in DisplayCache and click Detect from Script.
# version: 1
# ============================================================================
# DisplayCache - Building Usage (example custom script)
# Lists upcoming meetings/reservations for the next N days.
#
# PARAMETERS (set on the Custom display in DisplayCache):
# days - Number of days ahead to look (default: 7)
# limit - Max rows to return (default: 50)
# ============================================================================
#API
import json
import sys
# Guard: running outside TouchPoint? Bail out cleanly.
try:
_check = Data
except NameError:
print("This script is designed to run inside TouchPoint.")
sys.exit(0)
try:
from datetime import datetime, timedelta
days = int(Data.days) if hasattr(Data, 'days') and Data.days else 7
limit = int(Data.limit) if hasattr(Data, 'limit') and Data.limit else 50
start = datetime.now()
end = start + timedelta(days=days)
sql = '''
SELECT TOP (@limit)
m.MeetingId,
o.OrganizationName AS [involvement],
m.MeetingDate,
m.Location AS [location],
m.Description AS [description]
FROM dbo.Meetings m
JOIN dbo.Organizations o ON o.OrganizationId = m.OrganizationId
WHERE m.MeetingDate BETWEEN @start AND @end
ORDER BY m.MeetingDate ASC
'''
rows = q.QuerySql(sql, {
'limit': limit,
'start': start,
'end': end,
})
results = []
for row in rows:
when = row.MeetingDate.strftime("%a %b %d %I:%M %p") if row.MeetingDate else ""
results.append({
"involvement": row.involvement or "",
"location": row.location or "TBD",
"starts_at": when,
"description": row.description or "",
})
print("<!--DC_START-->")
print(json.dumps(results))
print("<!--DC_END-->")
except:
e = sys.exc_info()
print("<!--DC_START-->")
print(json.dumps({"error": str(e[1])}))
print("<!--DC_END-->")
What it produces
After clicking Detect from Script, the Columns table will fill with involvement, location, starts_at, and description. Pick a Display Format (Cards is a good default), set a Display Title like “This Week at Church,” and click Save & Publish.
Tweaking it
- Filter by location: add
AND m.Location LIKE '%Sanctuary%'to the WHERE clause, or accept alocationparameter. - Filter by involvement: pass
oidas a parameter, then addAND m.OrganizationId = @oid. - Hide private meetings: add
AND o.IsHidden = 0.
Troubleshooting
| Symptom | What to check |
|---|---|
| "Script returned no data to detect columns from" | The script ran but printed an empty list. Confirm the SQL returns rows in TouchPoint's SQL tab; then double-check your days / oid parameters in DisplayCache. |
| "Detection failed: ..." | The script raised an error before printing the JSON. Open the script in TouchPoint and run it from there — the error message will appear directly. Common causes: missing parameter, SQL typo, referencing a column that doesn't exist on a row. |
| Columns detected but Preview is blank | Likely a key mismatch. Open the Raw data tab of the preview and confirm the JSON keys match the columns you defined. |
| Photos / images not loading | For TouchPoint people photos, use the field name picture_url — DisplayCache rewrites that one through an authenticated proxy. Other image URLs must be publicly reachable. |
| "Access denied to PythonAPI" | The connected TouchPoint user is missing the APIOnly role. Email TouchPoint support to enable it — usually one business day. |
Need help building a script? Reach out — we're happy to look at your TouchPoint schema and sketch one for you.