דלגו לתוכן

או התחילו מנקודה בטוחה: אינדקס הכלים המלא · מפת הטבלאות המרכזיות

מערכת חדשה — 14 יום חינם

קבצים ב‑REST

עודכן 30.08.2026

העלאת קובץ למערכת היא שתי פעולות נפרדות, לא אחת:

  1. העלאה — הבייטים עולים לאחסון והשרת מחזיר name ו‑url.
  2. שיוך — כותבים את הערך שחזר לתוך שדה מסוג File ברשומה.

בין שתי הפעולות אין קשר אוטומטי. זו הנקודה שממנה נובעות כמעט כל התקלות בעבודה עם קבצים.

POST /parse/files/{fileName}

הגוף הוא בייטים גולמיים; כותרת Content-Type היא של הקובץ, לא application/json.

Bash
curl -sS -X POST "https://api.mbapps.co.il/parse/files/contract.pdf" \
-H "X-Parse-Application-Id: $APP_ID" \
-H "X-Parse-Master-Key: $MASTER_KEY" \
-H "Content-Type: application/pdf" \
--data-binary '@/path/to/contract.pdf'
JavaScript
const fs = require('fs');
const APP_ID = process.env.APP_ID, MASTER_KEY = process.env.MASTER_KEY;
const res = await fetch('https://api.mbapps.co.il/parse/files/contract.pdf', {
method: 'POST',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Master-Key': MASTER_KEY,
'Content-Type': 'application/pdf',
},
body: fs.readFileSync('/path/to/contract.pdf'),
});
const data = await res.json();
Python
import os, requests
APP_ID = os.environ["APP_ID"]
MASTER_KEY = os.environ["MASTER_KEY"]
with open("/path/to/contract.pdf", "rb") as fh:
res = requests.post(
"https://api.mbapps.co.il/parse/files/contract.pdf",
headers={
"X-Parse-Application-Id": APP_ID,
"X-Parse-Master-Key": MASTER_KEY,
"Content-Type": "application/pdf",
},
data=fh,
)
data = res.json()
PHP
<?php
$appId = getenv('APP_ID');
$masterKey = getenv('MASTER_KEY');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.mbapps.co.il/parse/files/contract.pdf",
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Parse-Application-Id: $appId",
"X-Parse-Master-Key: $masterKey",
"Content-Type: application/pdf",
],
CURLOPT_POSTFIELDS => file_get_contents("/path/to/contract.pdf"),
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);

201 Created

JSON
{
"url": "https://api.mbapps.co.il/parse/files/aaaaaaa…/9f2c1b7e-contract.pdf",
"name": "9f2c1b7e-contract.pdf"
}
פרט ערך
גודל מרבי אין תקרה נאכפת. העלאות של 11MB ו‑15MB נשמרות
סוג הקובץ יש ולידציה בצד השרת — לפי התוכן (magic bytes) — ראו האזהרה למטה
Content-Type אינו נבדק. PNG תקין שנשלח עם Content-Type: text/plain נשמר (201). שלחו את הטיפוס הנכון בכל זאת — הוא מה שהדפדפן יקבל בהורדה
שם הקובץ בנתיב כל שם מתקבל — גם רווחים, סוגריים ועברית (בקידוד URL). השרת מוסיף קידומת ומקודד את השם ב‑url שחוזר
אימות אינו נדרש להעלאה — X-Parse-Application-Id בלבד מספיק
Bash
curl -sS -X PUT "https://api.mbapps.co.il/parse/classes/Cases/aB3dEf9HiJ" \
-H "X-Parse-Application-Id: $APP_ID" \
-H "X-Parse-Master-Key: $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"Attachment":{"__type":"File","name":"9f2c1b7e-contract.pdf","url":"https://api.mbapps.co.il/parse/files/aaaaaaa…/9f2c1b7e-contract.pdf"}}'
JavaScript
const APP_ID = process.env.APP_ID, MASTER_KEY = process.env.MASTER_KEY;
const res = await fetch('https://api.mbapps.co.il/parse/classes/Cases/aB3dEf9HiJ', {
method: 'PUT',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Master-Key': MASTER_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
Attachment: {
__type: 'File',
name: '9f2c1b7e-contract.pdf',
url: 'https://api.mbapps.co.il/parse/files/aaaaaaa…/9f2c1b7e-contract.pdf',
},
}),
});
const data = await res.json();
Python
import os, requests
APP_ID = os.environ["APP_ID"]
MASTER_KEY = os.environ["MASTER_KEY"]
res = requests.put(
"https://api.mbapps.co.il/parse/classes/Cases/aB3dEf9HiJ",
headers={
"X-Parse-Application-Id": APP_ID,
"X-Parse-Master-Key": MASTER_KEY,
"Content-Type": "application/json",
},
json={
"Attachment": {
"__type": "File",
"name": "9f2c1b7e-contract.pdf",
"url": "https://api.mbapps.co.il/parse/files/aaaaaaa…/9f2c1b7e-contract.pdf",
},
},
)
data = res.json()
PHP
<?php
$appId = getenv('APP_ID');
$masterKey = getenv('MASTER_KEY');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.mbapps.co.il/parse/classes/Cases/aB3dEf9HiJ",
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Parse-Application-Id: $appId",
"X-Parse-Master-Key: $masterKey",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"Attachment" => [
"__type" => "File",
"name" => "9f2c1b7e-contract.pdf",
"url" => "https://api.mbapps.co.il/parse/files/aaaaaaa…/9f2c1b7e-contract.pdf",
],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);

לפני זה — ודאו שהשדה קיים ושהוא באמת מטיפוס File או PrivateFile:

Bash
curl -sS "https://api.mbapps.co.il/parse/schemas/Cases" \
-H "X-Parse-Application-Id: $APP_ID" -H "X-Parse-API-Key: $API_KEY"
JavaScript
const APP_ID = process.env.APP_ID, API_KEY = process.env.API_KEY;
const res = await fetch('https://api.mbapps.co.il/parse/schemas/Cases', {
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-API-Key': API_KEY,
},
});
const data = await res.json();
Python
import os, requests
APP_ID = os.environ["APP_ID"]
API_KEY = os.environ["API_KEY"]
res = requests.get(
"https://api.mbapps.co.il/parse/schemas/Cases",
headers={
"X-Parse-Application-Id": APP_ID,
"X-Parse-API-Key": API_KEY,
},
)
data = res.json()
PHP
<?php
$appId = getenv('APP_ID');
$apiKey = getenv('API_KEY');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.mbapps.co.il/parse/schemas/Cases",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Parse-Application-Id: $appId",
"X-Parse-API-Key: $apiKey",
],
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);

ראו סכימות.

File PrivateFile
מה קורה בשיוך ה‑URL נכתב מחדש לכתובת ציבורית: https://db-static.mbapps.co.il/<APP_ID>/<name>, והערך מקבל "public": true ה‑URL נכתב מחדש ל‑…/parse/files/<APP_ID>/<name>/<Table>/<objectId>/<Field>, והערך מקבל "private": true
הגישה פתוחה לכל מי שמחזיק את הכתובת200 בלי שום כותרת (S3 + CDN, Cache-Control: public, max-age=86400) מתווכת דרך הרשומה והשדה: Master Key, או Session Token של משתמש עם הרשאה על הרשומה. כל השאר — 302 לשער ההתחברות
מתאים ל לוגו, תמונת מוצר, נכס שיווקי חוזים, מסמכים חתומים, מסמכים חשבונאיים, כל דבר עם מידע אישי
דוגמאות במוצר _User.profile_image, Products.PreviewImage, Products.Image1/Image2, Files.PublicFile PriceQuotes.SignedPdf, PriceQuotes.Signature, AccountingHeaders.File, Notes.File, Files.File, ConversationMessages.File
1 · POST /parse/files/{name}  ──>  { name, url }
                               │
              crash here │  ──>  orphan file
                               ▼
2 · PUT /parse/classes/{Table}/{objectId}

תהליך שקרס אחרי ההעלאה ולפני השיוך משאיר קובץ יתום: הוא קיים, הוא צורך מכסת אחסון, ואף רשומה אינה מצביעה עליו. אין דרך למצוא אותו דרך ה‑API — הוא אינו מופיע בשום שאילתת נתונים.

הדפוס העמיד: רשמו את ה‑url שחזר לפני ניסיון השיוך, כדי שיהיה מה לשייך או מה לנקות אחר כך.

Python
import logging, requests
API = "https://api.mbapps.co.il/parse"
H_BIN = {"X-Parse-Application-Id": APP_ID, "X-Parse-Master-Key": MASTER_KEY}
H_JSON = {**H_BIN, "Content-Type": "application/json"}
def upload_and_attach(path: str, mime: str, table: str, object_id: str, field: str) -> dict:
filename = path.rsplit("/", 1)[-1]
# 1 · העלאה
with open(path, "rb") as fh:
up = requests.post(f"{API}/files/{filename}", headers={**H_BIN, "Content-Type": mime},
data=fh, timeout=120)
up.raise_for_status()
stored = up.json() # {"name": "...", "url": "..."}
# תיעוד לפני השיוך — כדי שקובץ יתום לא ייעלם מהעין
logging.info("uploaded %s -> %s", filename, stored["url"])
# 2 · שיוך
body = {field: {"__type": "File", "name": stored["name"], "url": stored["url"]}}
at = requests.put(f"{API}/classes/{table}/{object_id}", headers=H_JSON, json=body, timeout=30)
if not at.ok:
logging.error("attach failed for %s — orphan file remains at %s", object_id, stored["url"])
at.raise_for_status()
return stored
השכבה ההתנהגות
web2table / web2case לא תומכות בהעלאת קבצים — ושדה מסוג File בבקשה אינו “נזרק בשקט” אלא מפיל את כל הבקשה ב‑400, ומשאיר אחריו נזק: איש הקשר כן נוצר, ואם שלחתם אובייקט File (ולא מחרוזת) גם נוצרות עד 5 שורות יעד כפולות בגלל retry פנימי. העלו בנפרד ושייכו אחר כך, או קבלו את הקובץ בשרת ביניים משלכם. הכלל חל גם על getlead
WhatsApp תחבורה משלו — base64 עם קידומת Data‑URI, או שם קובץ שכבר שמור במערכת. שימו לב לאסימטריה: העלאה רגילה מקבלת base64 בלי קידומת
שדה String שנראה כמו URL לפעמים עמוד מצפה ל‑URL גולמי במחרוזת ולא לאובייקט File. בדקו סכימה
DELETE /parse/files/{fileName}

הפעולה קיימת ועובדת, ודורשת Master Key או API Key:

Bash
curl -sS -X DELETE "https://api.mbapps.co.il/parse/files/9f2c1b7e-contract.pdf" \
-H "X-Parse-Application-Id: $APP_ID" \
-H "X-Parse-Master-Key: $MASTER_KEY"
JavaScript
const APP_ID = process.env.APP_ID, MASTER_KEY = process.env.MASTER_KEY;
const res = await fetch('https://api.mbapps.co.il/parse/files/9f2c1b7e-contract.pdf', {
method: 'DELETE',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Master-Key': MASTER_KEY,
},
});
Python
import os, requests
APP_ID = os.environ["APP_ID"]
MASTER_KEY = os.environ["MASTER_KEY"]
res = requests.delete(
"https://api.mbapps.co.il/parse/files/9f2c1b7e-contract.pdf",
headers={
"X-Parse-Application-Id": APP_ID,
"X-Parse-Master-Key": MASTER_KEY,
},
)
PHP
<?php
$appId = getenv('APP_ID');
$masterKey = getenv('MASTER_KEY');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.mbapps.co.il/parse/files/9f2c1b7e-contract.pdf",
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Parse-Application-Id: $appId",
"X-Parse-Master-Key: $masterKey",
],
]);
$response = curl_exec($ch);
curl_close($ch);
  • הצלחה: 200 עם גוף ריק — לא {}, לא הודעה.
  • אחרי המחיקה, קריאה ל‑URL של הקובץ תחת /parse/files/ מחזירה 404 File not found.
  • שם קובץ שאינו קיים מוחזר גם הוא ב‑200. הפעולה אידמפוטנטית ואינה מבחינה בין “נמחק” ל“לא היה”.
  • עם X-Parse-Application-Id בלבד: 403 {"error":"unauthorized: master key is required"}.
  • אין כלי מחיקת קבצים בשכבת ה‑MCP — שם קיימים רק Upload-Public-File ו‑Get-File-Content.

השם למחיקה הוא ה‑name שחזר מהשרת (עם הקידומת), לא שם הקובץ המקומי שלכם. שמרו אותו.

מכאן נובע כלל תכנון: תכננו לא לייצר קבצים יתומים — לא כי אי אפשר למחוק, אלא כי אי אפשר למצוא אותם: אין נקודת קצה שמחזירה רשימת קבצים, והם אינם מופיעים בשום שאילתת נתונים.

האחסון נמדד לכל מערכת ומוצג בסביבת הפיתוח, במסך הפרויקט (“Storage used · X GB remaining”). לכל מערכת יש גם דגל outOfStorage. קריאת מצב האחסון ושינוי הדגל הן פעולות תפעוליות מצד הספק — הן אינן חשופות ב‑API הזה, ולכן גם אי אפשר לבדוק מהקוד כמה מקום נשאר. תכננו בהתאם: אם המערכת מעלה קבצים בקצב, בקשו מהצוות ניטור.

התבנית ההקשר
https://api.mbapps.co.il/parse/files/<APP_ID>/<FILE_NAME> URL קובץ כפי שחוזר מההעלאה — סגור מאחורי שער ההתחברות
https://db-static.mbapps.co.il/<APP_ID>/<FILE_NAME> File אחרי שיוך — עותק ציבורי ב‑CDN, מצורף לו "public": true
…/parse/files/<APP_ID>/<FILE_NAME>/<Table>/<objectId>/<Field> PrivateFile אחרי שיוך — הנתיב נכתב מחדש ומצורף לו "private": true
https://siteadmin.mbapps.co.il/uploads/… נכסי אתר — לוגואים, קבצי עמוד
  1. השדה קיים בסכימה, והוא File או PrivateFile — לא String.
  2. התוכן תואם לסיומת — אחרת 403 Invalid file type (ה‑Content-Type עצמו אינו נבדק, אבל שלחו אותו נכון).
  3. השיוך משתמש ב‑name ו‑url שחזרו מהשרת — שיוך עם השם המקומי מתקבל ב‑200, וההורדה מהכתובת שנכתבה מחדש מחזירה 404 File not found.
  4. מידע רגיש → PrivateFile, לא File — שדה File הופך את הקובץ לציבורי. מחיקה מסירה אותו מעתה (403), אך ה‑CDN עשוי לשמור עותק עד כ‑24 שעות.
  5. רשמתם את ה‑URL לפני השיוך, כדי שקריסה לא תשאיר קובץ אבוד ובלתי נראה.
  6. אם ההעלאה היא מקוד שרץ בדפדפן — זכרו שהיא עוברת גם בלי מפתח, כלומר גם לתוקף.